mybatis学习
mybatis框架:是一个框架,早期叫做ibatis,代码在github
mybatis是MyBatis SQL Framework for Java(sql映射框架)
-
sql mapper:sql映射
可以把数据库中的一行数据映射为一个java对象。
一行数据可以看做是一个java对象。操作这个对象就相当于操作表中的数据
-
Data Access Objects(DAOs):数据访问,对数据库执行增删改查。
mybatis提供的功能:
- 提供了创建Connection,Statement,ResultStet的能力,不用开发人员创建了
- 提供了执行sql语句的能力,不用自己执行sql
- 提供了循环sql,把sql的结果转换为java对象,List集合的能力
- 提供了关闭资源的能力,不用自己关闭Connection,Statement,ResultStet
开发人员做的就是:提供sql语句
最后是:
开发人员提供sql语句–mybatis处理sql–开发人员得到List集合或java对象(表中的数据)
可以查mybatis官方文档
https://mybatis.org/mybatis-3/zh/getting-started.html
mybatis实现步骤:
1、新建student表
2、加入maven的mybatis依赖和mysql驱动的依赖
3、创建实体类,Student
4、创建持久层接口
5、创建一个mybatis使用的配置文件 叫做sql映射文件:写sql语句一般一个表一个sql映射文件,这个文件是xml文件 1、在接口所在的目录中
2、文件名称和接口保持一致
6、创建mybatis的主配置文件 一个项目就是一个主配置文件 主配置文件提供了数据库的连接信息和sql映射文件位置信息
7、创建使用mybatis类 通过mybatis访问数据库
一般每一个接口配一个xml配置文件sql映射文件:写sql语句的,mybatis会执行这些
1、指定约束文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
mybatis-3-mapper.dtd :是约束文件名称,扩展名是dtd的。
2、约束文件作用:限制,检查在当前文件中出现的标签,属性必须符合mybatis的要求
3、mapper 是当前文件的根标签,必须是这个! namespace 命名空间,唯一值,可以使自定义的字符串要求你使用dao接口的 全限定(是从根包开始)名
4、在当前文件中,可以使用当前特定的标签,表示数据库的特定操作
<select>:表示执行查询,select语句
<update>:表示更新数据库的操作,写的是update操作 <insert>:表示插入,放的是insert语句
<delete>:表示删除,执行的是delete语句
<mapper namespace="org.example.dao.StudentDao"> <!-- <select>:表示查询操作 id:要执行的sql 语法的唯一标识,mybatis会使用这个id的值来找到 要执行的sql语句 可以自定义,但是要求使 用的接口中的方法名称 resultType:表示结果 类型的,是sql语句执行后得到ResultSet,遍历这 个ResultSet得到这个java对象的类型 值写的是类型的全限定名称--> <select id="selectStudent" resultType="org.example.domain.Student"> select id,name,email,age from student order by id
</select>
</mapper>
开启日志:
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
开启日志有助于调试及查看错误
主要类的介绍
-
Resources:mybatis中的一个类,负责读取主配置文件
//读取这个config表示的文件 InputStream input = Resources.getResourceAsStream(config);
-
SqlSessionFactoryBuilder:用来创建SqlSessionFactory对象
//创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //创建创建SqlSessionFactory对象 SqlSessionFactory factory = builder.build(input);
-
SqlSessionFactory:重量级对象,程序创建一个对象耗时比较长,使用资源较多,在整个项目中,有一个就够了
SqlSessionFactory作用:获取SqlSession对象
SqlSession sqlSession = factory.openSession();
openSession()方法说明:
- openSession():无参数的,获取到的是非自动提交事务的SqlSession对象
- openSession(boolean):
- openSession(true) 获取自动提交事务的SqlSession
- openSession(false) 获取非自动提交事务的SqlSession
-
SqlSession:
-
SqlSession接口:定义了操作数据的方法,例如 selectOne(),selectList(),insert(),update(),delete(),commit(),rollback()
SqlSession接口的实现类DefaultSqlSession
使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSesion()获取SqlSession,在执行完sql语句后,需关闭它,执行SqlSession.close(),这样能保证它的使用是线程安全的
-
-
使用mybatis的动态代理机制,使用SqlSession.getMapper()(dao接口)
getMapper能获取dao接口的对应的实现类对象
调用方法需注意:
List studentList = studentDao.selectStudent();的调用
1、dao对象,类型是StudentDao,权限定名称是:com.greatwallwcc.dao.StudentDao,全限定名称和namespace是一样的,此为切入点
2、方法名称,selectStudent,这个方法就是mapper文件中的id值selectStudent,据此可以直接找到.xml文件中的方法
3、通过dao中方法的返回值也可以确定Mybatis要调用的SqlSession的方法, 如果返回值是list,调用的是SqlSession.selectList()方法,如果返回值是int或者是非list的其他类型,看mapper文件中的标签,例如或者是,就会调用相应的insert或者是update等方法mybatis的动态代理:mybatis根据到的方法调用,获取执行sql语句的信息,mybatis根据你的dao接口,创建出一个到的接口实现类,并创建这个类的对象,完成SqlSession调用方法来访问数据库
mybatis传参:
-
parameterType:dao接口中的方法参数的数据类型 parameterType:它的值是java的数据类型的全限定名称或者是mybatis定义的别名 parameterType=“java.lang.Integer” 别名为 parameterType="int"注意: parameterType不是强制的,mybatis通过反射机制能够发现接口参数的数据类型 所以可以没有,一般也不写
mapper中获取简单参数的方法,用占位符 #{}
如:
Student selectStudentById(Integer id);
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.greatwallwcc.domain.Student"> select * from student where id=#{id};</select>
传参方式:
-
简单传参,直接使用传过来的形参名(mapper中获取简单参数的方法,用占位符 #{})
-
多个参数使用@param命名,用注解(多个参数:命名参数,在形参定义的前面加入@param(“自定义参数名称”) )
List<Student> selectMulParam(@Param("myName") String name,@Param("myAge") Integer age); <select id="selectMulParam" resultType="com.greatwallwcc.domain.Student"> select * from student where name=#{myName} or age=#{myAge}; </select>
-
多个参数( 多个参数,使用java对象作为接口中的方法的参数 ),使用java对象的属性值,作为参数的实际值 适用对象语法:#{属性名,javaType=类型名称,jdbcType=数据类型} javaType:指java中的属性数据类型 jdbcType:在数据库中的数据类型 使用范例:
List<Student> selectMultiObject(QueryParam param); //本方法在调用的时候要创建一个类,必须包括paramName,和paramAge这两个参数,否则xml问价无法定位属性名 <select id="selectMultiObject" resultType="com.greatwallwcc.domain.Student"> select * from student where name=#{paramName} or age=#{paramAge};</select>
-
使用位置传参(使用#{arg0},#{arg1}代表第一个参数与第二个参数)
List<Student> selectMulPosition(String name,Integer age); <select id="selectMulPosition" resultType="com.greatwallwcc.domain.Student"> select * from student where name=#{arg0} or age=#{arg1}; </select>
-
使用map传参(键值对形式):返回值只能是一行记录,不能返回多行记录,若返回多行则会报错
List<Student> selectStudentByMap(Map<String,Object> map); //此处是调用的部分代码 Map<String,Object> map = new HashMap<>(); map.put("myName","小羊屎蛋儿"); map.put("myAge",3); <select id="selectStudentByMap" resultType="com.greatwallwcc.domain.Student"> select * from student where name=#{myName} or age=#{myAge}; </select>
若查询时,自己定义的属性名和数据库中的列名不一致的时候,可以使用select标签中的resultMap属性来解决,resultMap是用来指定列名和java对象的属性对应关系,**主要用于属性名和列名不一致的情况下,**以下为使用步骤:
- 先定义resultMap
- 在select标签中使用resultMap属性来引用已经定义的resultMap进行查询
/** * 使用resultMap获取全部数据 * 返回的是Map类型的List,不是直接返回Map * */ List<Student> selectAllStudent();
<!--使用resultMap1.先定义resultMap2.在select标签中,使用resultMap来引用 1 中定义的--> <!--定义resultMap 1.id:自定义名称,表示定义的这个resultMap 2.type:java类型的全限定名称--> <resultMap id="studentMap" type="com.greatwallwcc.domain.Student"> <!--内部写数据库列名和java属性名的关系(相当于将二者链接起来)--> <!--注解列,使用id标签 column:列名 property:java类型的属性名 --> <id column="id" property="id"></id> <!--主键列使用id标签,非主键列使用result标签--> <result column="name" property="name"></result> <result column="email" property="email"></result> <result column="age" property="age"></result> </resultMap> <!--使用resultMap属性来引用定义的resultMap--> <select id="selectAllStudent" resultMap="studentMap"> select * from student; </select>
也可以自由指定对用关系,比如把查询出来的email值赋值给name属性
<result column="email" property="name"> </result>
-
java中的包名问题
VO:值对象、视图对象
PO:持久对象
QO:查询对象
DAO:数据访问对象——同时还有DAO模式
DTO:数据传输对象——同时还有DTO模式
PO:全称是persistant object持久对象最形象的理解就是一个PO就是数据库中的一条记录。好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。
BO:全称是business object:业务对象主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。比如一个简历,有教育经历、工作经历、社会关系等等。我们可以把教育经历对应一个PO,工作经历对应一个PO,社会关系对应一个PO。建立一个对应简历的BO对象处理简历,每个BO包含这些PO。这样处理业务逻辑时,我们就可以针对BO去处理。
VO :value object值对象ViewObject表现层对象主要对应界面显示的数据对象。对于一个WEB页面,或者SWT、SWING的一个界面,用一个VO对象对应整个界面的值。
DTO :Data Transfer Object数据传输对象主要用于远程调用等需要大量传输对象的地方。比如我们一张表有100个字段,那么对应的PO就有100个属性。但是我们界面上只要显示10个字段,客户端用WEB service来获取数据,没有必要把整个PO对象传递到客户端,这时我们就可以用只有这10个属性的DTO来传递结果到客户端,这样也不会暴露服务端表结构.到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO。
POJO :plain ordinary java object 简单java对象个人感觉POJO是最常见最多变的对象,是一个中间对象,也是我们最常打交道的对象。一个POJO持久化以后就是PO直接用它传递、传递过程中就是DTO直接用来对应表示层就是VO
DAO:data access object数据访问对象这个大家最熟悉,和上面几个O区别最大,基本没有互相转化的可能性和必要.主要用来封装对数据库的访问。通常和PO结合使用,DAO中包含了各种数据库的操作方法。它可以把POJO持久化为PO,用PO组装出来VO、DTO
mybatis中占位符中的比较
-
占位符 # :告诉mybatis使用实际的参数值代替。并使用PrepareStatement对象执行sql语句,#{ … }对用sql语句中的 “?”,可以防止 sql 注入,更安全,效率高,较常使用
-
占位符$:与 # 的使用规则相同,它到了数据库中就运行成为了 String类型的sql语句 + “要查询的参数”,这种语法格式是字符串的拼接形式,并且你写的sql语句一定要符合sql语法,比如字符串:sql语句规定 select * from student where name=‘wcc’; 所以此时一定要注意传入的一定要是这种形式的’wcc’,而不能是wcc这种形式。并且也不是使用“?”占位符来占位,使用的是Statement对象执行sql,效率没有PrepareStatement高,安全性低,可能会出现sql注入,
-
占位符$可用来替换列名/表名:
/** * 使用$替换列名 * */List<Student> selectStudentOrder(@Param("colName") String colName);
<!--使用$替换列名,可用于排序--> <select id="selectStudentOrder" resultType="com.greatwallwcc.domain.Student"> select * from student order by ${colName}; </select>
运行时日志大打印出的sql语句为:
Preparing: select * from student order by id;
-
resultType:可以是权限定名称,也可以是别名
如 resultType=“java.lang.Integer”的别名为:
resultType=“int”
最好用全限定名称!
resultType的默认原则是,同名的列值赋值给同名的属性,所以当属性名和列名不一致的时候,还可以使用列的别名,使列名和属性名保持一致
<!--属性名和列名不同的情况,使用列的别名(第二种处理方式)--> <select id="selectDiffColAndProp" resultType="com.greatwallwcc.domain.MyStudent"> select id as myId,name as myName,email as myEmail,age as myAge from student;</select>
-
resultType自定义类型的别名:
必须在mybatis的主配置文件中进行配置
位置:在标签内
**方式1:**指定一个自定义别名
<typeAliases> <typeAlias type="com.greatwallwcc.domain.Student" alias="Student"> </typeAlias> <!--可以指定一个自定义别名 type:自定义类型的权限定名称 alias:别名(短小,易记) --> </typeAliases>
**方式2:**以包的方式(比较方便):类名就是别名
<!--name:包名,这个包中类的类名就是别名(类名不区分大小写),意思是直接以类的名字作为别名--> <package name="com.greatwallwcc.domain"/>
但是以上两种方式都不建议使用,因为不太安全,建议使用全限定名称,因为如果有两个包名相同且包含的类相同的话,使用时就无法区分是哪一个对象了,会产生歧义
mybatis的模糊查询(第一种更好用)
- 方式一:在java代码中指定like内容
/** * 第一种模糊查询(在java代码中,指定like内容) * */
@Test
public void testSelectLikeOne(){ SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name = "i";
List<Student> students = dao.selectLikeOne("%"+name+"%");
students.forEach(stu-> System.out.println(stu));}
<!--第一种模糊查询(在java代码中,指定like内容)-->
<select id="selectLikeOne" resultType="com.greatwallwcc.domain.Student">
select * from student where name like #{name}
</select>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PFknsVfJ-1614527062047)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1614156038014.png)]
-
方式二:在mapper文件中拼接 like 的内容
<!--第二种模糊查询(在mapper文件中拼接 like 的内容)--> <select id="selectLikeSecond" resultType="com.greatwallwcc.domain.Student"> select * from student where name like "%" #{name} "%"; </select>
格式不能写错,必须是 “%” #{Parma} “%”,千万不要忘记写 % 上面的 “ ”,也不要忘记加空格
动态SQL:动态sql标签包含的内容是从上往下运行,并且是逐行添加语句
-
标签:用来判断条件
语法:
<if test="判断java对象的属性值"> 部分sql语句 </if>
例如:
Student student = new Student(); student.setName("西八"); student.setAge(32); List<Student> students = dao.selectStudentByIf(student);
<select id="selectStudentByIf" resultType="com.greatwallwcc.domain.Student"> select * from student where <if test="name != null and name != ''"> name = #{name} </if> <if test="age > 10"> and age > #{age} </if> </select>
但是,如果 没有设置这一行 student.setName(“西八”); 而是直接查年龄,如:
Student student = new Student(); //student.setName("西八"); student.setAge(32); List<Student> students = dao.selectStudentByIf(student);
就会出现sql语句合成错误,然后报错的情况
select * from student where and age > ?
因为它把名字那一行直接省略去了,不运行,导致where与age之间多了个and
解决方法为:在where后面加一个恒等式,使得and可以连接两个条件
<select id="selectStudentByIf" resultType="com.greatwallwcc.domain.Student"> select * from student where 1=1 <if test="name != null and name != ''"> name = #{name} </if> <if test="age > 10"> and age > #{age} </if> </select>
-
标签:
语法格式:…
用where标签的好处是不用担心上述标签中出现的问题,因为标签会自动把多余的and或者or删除掉,如例所示:
<select id="selectStudentByWhere" resultType="com.greatwallwcc.domain.Student"> select * from student <where> <if test="name != null and name != ''"> name = #{name} </if> <if test="age > 10"> and age > #{age} </if> </where> </select>
Student student = new Student(); //student.setName("西八"); student.setAge(32); List<Student> students = dao.selectStudentByIf(student);
运行的sql语句为:select * from student WHERE age > ?
-
标签:是用来循环java中的数组,list集合的。主要用在sql的in语句中(因为in语句需要多个值作为参数来进行查询,所以可以用foreach标签从List集合中逐个遍历所需要的值),不用再自己写遍历了
格式:
<foreach collection="" item="" open="" close="" separator=""></foreach>
collection:表示接口中方法参数的类型,如果是数组使用array,如果是list集合使用list
item:自定义的,表示数组和集合成员的变量 (所要循环的内容)
open:循环开始时的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
第一种:遍历值 List ,而不是对象
<select id="selectStudentByForeach" resultType="com.greatwallwcc.domain.Student"> select * from student where id in <foreach collection="list" item="myid" open="(" close=")" separator=","> #{myid} </foreach> </select>
List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(102); list.add(1111); List<Student> students = dao.selectStudentByForeach(list);
运行的sql语句为:select * from student where id in ( ? , ? , ? , ? )
第二种:遍历对象 List
<!--动态sql,使用java对象作为参数,使用foreach标签--> <select id="selectStudentByForeach2" resultType="com.greatwallwcc.domain.Student"> select * from student where id in <foreach collection="list" item="stu" open="(" close=")" separator=","> #{stu.id} </foreach> </select>
Student student1 = new Student();Student student2 = new Student(); student1.setId(1); student2.setId(2); List<Student> list = new ArrayList<>(); list.add(student1); list.add(student2); List<Student> students = dao.selectStudentByForeach2(list);
运行的sql代码为:
==> Preparing: select * from student where id in ( ? , ? , ? , ? )
==> Parameters: 1(Integer), 2(Integer), 111(Integer), 101(Integer)==> :表示输入
只不过此时的遍历对象变为了对象中的一个属性,所以只需要在里面遍历属性即可
foreach标签遍历时还有一种写法,这种方法可以不使用foreach标签的close和open属性,自己在foreach标签的外面加 ( ) ,如:
<select id="selectStudentByForeach2" resultType="com.greatwallwcc.domain.Student"> select * from student where id in ( <foreach collection="list" item="stu" separator=","> #{stu.id} </foreach> ) </select>
SQL代码复用
位置:在所要使用本段复用代码片段的的mapper文件中,应位于标签内部,一般放于最上方
使用方法:
-
先定义 sql语句,表名,字段等
<!--sql代码复用--> <sql id="selectAll"> select * from student </sql>
-
再使用,
<select id="selectStudent" resultType="com.greatwallwcc.domain.Student"> <include refid="selectAll"></include> order by id; </select>
数据库的属性配置文件
-
把是数据库连接信息放到一个单独的文件中,和mybatis的zhu配置文件分开,这样会便于修改和处理多个数据库的信息
-
在resources目录中定义一个属性配置文件,文件后缀为 .properties ,在属性配置文件中,定义数据,格式是 key = value (key:一般是使用 . 做多级目录的)
-
jdbc.driver=com,mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm jdbc.username=root jdbc.password=047139
-
在mybatis的主配置文件中,使用指定文件的位置,在需要使用值的地方,${key},在主配置文件中的标签内添加一行:
<!--指定properties文件的位置,从类路径下开始找文件--> <properties resource="mybatis.properties" />
-
属性配置文件内容的引用
<environment id="myDev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--数据库的驱动类名--> <property name="driver" value="${jdbc.driver}"/> <!--连接数据库的URL字符串--> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment>
-
-
映射xml文件的方式:
<mappers> <!--第一种方式(指定多个mapper文件),这种方式需要对每一个xml文件写出全限定名称,当xml文件过多的时候,工作量太大--> <mapper resource="com/greatwallwcc/dao/StudentDao.xml"/> <!--第二种方式(使用包名),这个包中所有的xml文件都能一次加载给mybatis 限制: 1.mapper文件名称需要和接口名称一样,区分大小写 2.mapper文件和dao接口需要在同一目录 --> <package name="com.greatwallwcc.dao"/> </mappers>
数据分页:PageHelper(数据分页)
首先要清楚数据库中的分页是怎么完成的:分页在数据库中是用limit来完成的
select * from student limit 3,3;
<!--前面一个参数表示从第几行开始查,后一个参数表示需要几行数据-->
如果要使用mybatis进行分页的话,则需要添加以下依赖:
<!--pagehelper的依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId> <version>5.1.10</version>
</dependency>
还需要在主配置文件的标签内加入插件,如下:
<!--分页所用插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
使用方法如下,是在java代码中实现的:
<!--使用pagehelper分页查询所有数据-->
<select id="selectAllByPageHelper" resultType="com.greatwallwcc.domain.Student">
select * from student order by id
</select>
@Test
public void testSelectAllByPageHelper() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//加入pagehelper的方法,分页
//pageNum:从第几页开始,本例是从第2页开始
//pageSize:一页中有多少行数据,本例是一共3行
PageHelper.startPage(2,3);
List<Student> students = dao.selectAllByPageHelper();
students.forEach(stu -> System.out.println(stu));
> Preparing: select * from student order by id LIMIT ?, ?
> Parameters: 3(Integer), 3(Integer)
< Columns: id, name, email, age
< Row: 101, insertW, w@qq.com, 30
<== Row: 102, 西八, xiba@qq.com, 30
<== Row: 108, 小福贵, xiaofugui.163.com, 18
<== Total: 3
lectAllByPageHelper() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//加入pagehelper的方法,分页
//pageNum:从第几页开始,本例是从第2页开始
//pageSize:一页中有多少行数据,本例是一共3行
PageHelper.startPage(2,3);
List<Student> students = dao.selectAllByPageHelper();
students.forEach(stu -> System.out.println(stu));
==> Preparing: select * from student order by id LIMIT ?, ?
==> Parameters: 3(Integer), 3(Integer)
<== Columns: id, name, email, age
<== Row: 101, insertW, w@qq.com, 30
<== Row: 102, 西八, xiba@qq.com, 30
<== Row: 108, 小福贵, xiaofugui.163.com, 18
<== Total: 3
运行时sql语句中的两个问号分别是pageNum和pageSize