MyBatis
1.基础
1.介绍
是一个基于java的持久层框架,实现部分ORM功能.它是一个半自动ORM框架,Dao层实现可以不用自己写
2.优点
支持定制化SQL,存储过程以及高级映射设置 轻量级框架
可以使用简单的XML或注解来配置映射信息,和sql语句
2.架构图
3.入门案例
1.流程
-
引入3个jar包
a. log4j-1.2.17.jar
b. mybatis-3.4.6.jar
c. mysql-connector-java-5.1.30-bin.jar- 创建 resources 目录
a. 放入 log4j.properties 文件。 [文件包里直接导入]
b. 创建 mysql.properties 文件。 [设置好数据库连接参数]
c.创建 Mybatis.cfg.xml 文件
- 创建 resources 目录
-
创建实体类和数据表
-
创建UserMapper.xml 文件
2.相关代码
**1.**Mybatis.cfg.xml 文件
<!-- 1. 加载 mysql 属性参数文件 -->
<properties resource="mysql.properties"></properties>
<!-- 2.配置使用环境 defaulut="mysql" 默认使用数据库环境 -->
<environments default="mysql">
<environment id="mysql">
<!-- 配置JDBC事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 使用数据源连接池 -->
<dataSource type="POOLED">
<!-- 配置数据连接 4 要参,参数名是 mybatis 预设的, 你不要去动。-->
<property name="driver" value="${jdbc.driverName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置mappers标签 -->
<mappers>
<mapper resource="com/gec/domain/UserMapper.xml"/>
</mappers>
2.UserMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 上面是mapper.dtd 的模板内容 -->
<mapper namespace="com.gec.domain.UserMapper"> //命名空间要与mybatis.cfg.xml 中设置一致
<insert id="addUser"> //插入语句
insert into user(
id,username,password,nickName,age,email,createDate
)values(
null,#{username},#{password},#{nickName},#{age},#{email},now()
)
</insert>
</mapper>
3.测试类
1.加载Mybatis.cfg.xml
InputStream is = Resources.getResourceAsStream("Mybatis.cfg.xml");
2.使用SqlSessionFactory构建器,创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
3.通过会话工厂得到会话
Session session = factory.openSession();
4.设置statement全限定名
String namespace = "com.gec.domain.UserMapper";
String statement = namespace+".addUser";
5.使用会话执行CURD操作
int cnt = session.insert(statement,user);
6.提交事务
session.commit();
7.关闭会话
session.close();
4.增删改查
注意:statement == 命名空间+ .stateId
String namespace = “com.gec.domain.UserMapper”;
String statement = namespace+“.deleteUser”;
1.insert操作
- xml文件中使用标签
- 调用session的方法: session.insert(statement); /session.insert(statement,输入参数); //操作定位符
<insert id="addUser"> // xml文件中 使用 insert标签
insert into user(
id,username,password,nickName,age,email,createDate
)values(
null,#{username},#{password},#{nickName},#{age},#{email},now()
)
</insert>
操作定位符
session.insert(statement,user);
- 插入数据并得到生成的id
- keyProperty:告诉mybatis哪个是主键
- useGeneratedKeys:成功插入数据后,得到生成的主键值,并赋值给对象上
<insert id="addUserAndGetId" keyProperty="id" useGeneratedKeys="true"> //id为主键
insert into user(
id,username,password,nickName,age,email,createDate
)values(
null,#{username},#{password},#{nickName},#{age},#{email},now()
)
</insert>
2.delete操作
- xml文件中 使用 delete标签
- session.delete(stateId)/session.delete(stateId,入参) //没有设置占位符则不入参
xml文件中
<delete id="deleteUser">
delete from user where id=#{id}
</delete>
session.delete(statement, 9);
3.update操作
- xml文件中使用update标签
- session.update(statement);/session.update(statement, user);//没有设置占位符则不入参
<update id="updateUser">
update user set
username=#{username},password=#{password},nickName=#{nickName},age=#{age},
email=#{email}
where id=#{id}
</update>
session.update(statement, user)
注意:
- sql设置了占位参数,但对象属性为null时,执行完sql,记录对应字段会设置为null
- 属性值为基础类型时,没有赋值,对应字段设置为0/0.0,尽量不要设置为基本数据类型
- 不管任何标签都能做任何事,不管任何方法都能做任何事
- xml中设置了update标签,但是调用了session.selectOne()方法同样能修改数据库
- xml中设置了update标签,但是里面的sql语句为删除语句,此时是删除数据库数据
5.自动映射
1.从数据库到程序
1.原理
- 先找对应的setXXX方法,有则通过反射调用set方法设置Pojo属性
- 要是没有则找对应属性名,有则直接反射字段赋值,没有则为 null (不会报错)
- 类中要有无参构造器,否则会报错
2.从程序到数据库
1.原理
程序(内存) --> 写入到数据库
- 先找输入对象的对应getXXX方法,有则通过反射调用getXXX获取Pojo属性,并填入SQL
- 若无get方法,则看有无对应字段,有则直接反射获取字段值设置到SQL语句
- 要是都没有则报错
- 看有无get方法–>没有,看有无对应属性名–>没有,报错 //有则设置到SQL语句
6.输入类型入参
1.入参到哪里
使用 Map/Pojo 承载数据,并填入下面
<insert id="addUser">
insert into user(
id,username,password,nickName,age,email,createDate
)values(
null,#{username},#{password},#{nickName},#{age},#{email},now()
)
</insert>
2.入参方式
-
使用map承载数据(多个参数时),并用map入参
Map p = new HashMap(); p.put("id", 1); p.put("username", "林"); session.update(statement, p);
-
使用JavaBean承载数据(多个参数时),并用 属性名和get()方法入参(POJO入参)
Person p = new Person(); person.setId(2); person.setUsername("王"); session.update(statement, p);
-
使用基础数据与包装类承载数据入参(一个参数时)
7.输出类型出参
1.出参原理
2.出参方式
-
基础类型 //输出结果是基础类型
<select id="getPerson" resultType="string"> select username from person where id=#{id} </select>
-
Map类型 //输出结果是Map
<select id="getPersonToMap" resultType="java.util.Map"> select * from person where id=#{id} </select>
-
POJO类型
9.获取多条记录
1.使用事项
- selectOne:查询结果只有一条记录则使用这个,若有多条则会报错
- session.selectOne(statement,1); //查询id为1的Person
- selectList:查询结果有多条记录使用该方法
- List list = session.selectList(statement); //查询所有
- resultType=“list中装载的数据类型”
MyBatis动态代理开发
1.传统方法弊端
Dao层中实现类的getUserById方法要做的事 步骤太过繁琐
1.获取connection;2.编写sql语句;3.获取statement;4.执行sql;5.得到结果集;6.封装User,添加到List集合
7.关闭psmt,rs;8.返回List<User>
2.什么是动态代理开发
- 结合动态代理技术创建代理对象的生成技术,实现Dao接口层的实现类的自动创建,并通过Mybatis的API
自动关联Mapper映射器中的statement,从而实现与数据库的交互,便是Mybatis的动态代理开发模式.
- 要提供Mapper的操作接口,提供Mapper映射器配置文件或使用注解
3.遵循的开发约定
1.命名空间
namespace=“接口全类名” //com.gec.mapper.UserMapper
2.接口方法名
要与statementId一致
<select id="getUser" ...>
public User getUser(){...}
3.入参保证
在SQL中的占位参数,必须能从传入的参数中找到 #{XXX}
a.通过位置匹配(传入多个参数)
b.通过key匹配
c.通过getXXX匹配
d.通过属性名匹配
4.出参保证
保证类型一致
a.resultType=“泛型中类型”
b.resultType=“方法的返回类型”
配置格式
<mapper namespace="接口全类名">
<select id="接口方法名" resultType="返回类型/泛型类型">
select * from user where age>#{age} and username=#{username}
</select>
</mapper>
注意事项
- xml文件中statementID不能有相同的否则会报错
- 接口类中可以有重名方法,根据入参的数据选择调用哪个方法
4.多参数绑定
-
单个参数
#{占位符名称} 可以随意设置,#{a},#{b},#{c}
-
多个参数
-
索引入参
List<User> getUserByManyPara(String nickName, int age ); [arg0] [arg1] [param1] [param2] select * from user where nickName=#{ arg0 } and age > #{ arg1 }
-
注解入参
List<User> getUserByManyPara( @Param("_nickName")String nickName, @Param("_age")int age ); select * from user where nickName=#{ _nickName } and age > #{ _age } //>代表 > &t;代表 < resultType="别名"
-
MyBatis进阶
1.核心配置文件
1.别名设置
内置别名
Alias(别名) | Mapped type Java类型 |
---|---|
_byte | byte |
_long _short _int _integer | long/short/int/integer |
_double _float _boolean | double/float/boolean |
string /byte /boolean | String/Byte/Boolean |
long/short/int/integer | Long/Short/Integer/Integer |
double/float/date | Double/Float/Date |
map/hashmap | Map/HashMap |
list/arraylist | List/ArrayList |
自定义别名
格式:在mybatis.cfg.xml文件中
单独声明一个别名映射:
包扫描方式批量声明:
<typeAliases>
<typeAlias type="com.gec.domain.User" alias="user"/>
</typeAliases>
resultType="user"
2.映射文件注册
-
location=“xml地址”
-
map接口 class=“mapper地址”
-
包扫描方式
映射文件注册方式 | Mapper.xml文件是否要与接口名相同 | 是否要在同一个包 |
---|---|---|
location=“xml地址” | 不需要 | 不需要 |
class=“mapper地址” | 需要 | 需要 |
包扫描方式 | 需要 | 需要 |
2.结果集映射器
1.使用场景
当表列名与类属性名不一致时,需要提供映射处理器来作数据映射,我们可以自己定义一套映射规则
2.格式
<resultMap type="com.gec.domain.User" id="getHandler">
<id column="_id" property="id"/>
<result column="user_name" property="username"/>
<result column="_password" property="password"/>
</resultMap>
3.有参映射器
- 提供有参构造器–>提供对应映射处理器–>在标签中引用
<resultMap type="com.gec.domain.User" id="userResult">
<constructor>
<idArg column="_id" javaType="string"/>
<arg column="user_name" javaType="string"/>
<arg column="_password" javaType="string"/>
<arg column="_age" javaType="integer"/>
</constructor>
</resultMap>
==注意:==数据类型要与有参构造器里的一致,并且顺序也要一致!
3.参数映射器
4.动态SQL
1.什么是动态SQL
能够按照逻辑与规则,根据输入条件,来动态生成SQL语句
2.引用SQL片段
标签:设置SQL代码片段
标签:引用SQL代码片段
<sql id="field"> //设置SQL片段
id,username,password,age
</sql>
<sql id="values">
#{id},#{username},#{password},#{age}
</sql>
<insert id="getUserPar">
insert into user(
<include refid="field"/> //引用SQL片段
)values(
<include refid="values"/>
)
</insert>
3.if条件设置
sql语句
5.#号和$号
1.#号应用
“%”#{username}“%” --> 最终得到: ‘%andy%’
使用#号做占位符,入参的参数类型可以是字符串,user对象,Map…
<select id="getUser" resultType="com.gec.domain.User">
select * from user where age>#{age} and username="%"#{username}"%"
</select>
2.$号应用
‘%${username}%’ --> 最终得到: ‘%andy%’ //不用添加双引号
使用$ 号做占位符,要求传POJO对象,通过 ${username}取user的name属性
<select id="getUser" resultType="com.gec.domain.User">
select * from user where age>#{age} and username='%${username}%'
</select>
3.两者区别
1.只有单一参数
取参方式 | ${} | #{} |
---|---|---|
arg0 | 不行 | ok |
param1 | 不行 | ok |
KEY | 键不匹配不行,键匹配OK | 键不匹配ok,键匹配OK//${aa}不行 #{aa}ok |
2.有多个参数
取参方式 | ${} | #{} |
---|---|---|
arg0 | ok | ok |
param1 | ok | ok |
Key | 键匹配OK | 键匹配OK |
MyBatis表关联
1.一对多
1.配置说明
- 使用左外连接查询数据库
- 在 resultMap 中来封装部门基础数据。
- resultMap中使用来封装 List中的数据
- 中设置:
- 格式:<collection …节点属性项> 设置List所存储对象的属性
- property=“属性名”
- ofType=“Java类型”//泛型中的类型
- column=“外键列名”
<resultMap type="com.gec.domain.Dept" id="DeptResultHandler">
<id column="_id" property="id"/> //封装部门的基本数据
<result column="dept_name" property="deptName"/>
<result column="_descript" property="descript"/>
<collection property="emps" ofType="com.gec.domain.Employee"> //封装List中的数据
<id column="emp_id" property="id"/>
<result column="emp_name" property="empName"/>
</collection>
</resultMap>
2.多对一
1.association标签
- 功能:用来发出另外一个子查询
- 常规设置项
- peoperty=“属性名”
- column=“列名” 传入子查询的入参
- javaType=“入参类型”
- select=“statement_id” 另一条子查询
a.
- 单查员工表的数据 输出类型=“结果集处理器” -> EmployeeResultHandler
b.结果集处理器
- 封装基础数据
- association 关联另一条查询。
select 还可以关联另一个命名空间中查询。
c. 子查询
- 用来查询部门表的数据, 根据 id id 从 association 中的column=“xxx” 来的。
d. 在另外一个 resultMap 中封装部门数据。
a. <select id="getEmpById" resultMap="EmpResult">
select * from t_employee e
where e._id=#{id}
</select>
b. <resultMap type="com.gec.domain.Employee" id="EmpResult">
<id column="_id" property="id"/>
<result column="emp_name" property="empName"/>
<association property="dept"
column="dept_id" javaType="com.gec.domain.Dept"
select="queryDeptById"/>
</resultMap>
c. <select id="queryDeptById" resultMap="DeptResult">
select * from t_dept d
where d._id = #{id}
</select>
d. <resultMap type="com.gec.domain.Dept" id="DeptResult">
<id column="_id" property="id"/>
<result column="dept_name" property="deptName"/>
<result column="_descript" property="descript"/>
</resultMap>
3.多对多
a. 命名空间: com.gec.mapper.CourseMapper
b.
1)同时查询三表的数据
【1】t_course
【2】t_student_course
【3】t_student
优先查询 课程 表, 就算没有学生选修这门课程, 也能得到 [课程对象]。
2)选取数据时, 把两个 _id 去掉。
2)输出类型=“courseMap”
c. 结果集处理器 [courseMap]。
-
先封装 Course 基础数据 .选择中间表的 xx_id, 填入 id 项属性。
-
再封装 Collection 关联数据.选择中间表的 xx_id, 填入 id 项属性。
<select id="getCourceById" resultMap="CourceMap">
select sc.*,c.cource_name,s.student_name from t_cource c
left join t_student_cource sc on c.id = sc.crs_id
left join t_student s on s._id= sc.stu_id
where c.id=#{id}
</select>
<resultMap type="com.gec.domain.Cource" id="CourceMap">
<id column="crs_id" property="id"/>
<result column="cource_name" property="courceName"/>
<collection property="students"
ofType="com.gec.domain.Student"
column="stu_id">
<id column="stu_id" property="id"/>
<result column="student_name" property="studentName"/>
</collection>
</resultMap>
4.分页插件
1.介绍
分页插件是 mybatis 中用处理分页功能的功能插件, 作为一个插件来使用。
2.前期准备
1.jar包
2.mybatis.cfg.xml
放在别名标签后面,环境标签前面
<typeAliases>...</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql" />
<property name="offsetAsPageNum" value="false" />
<property name="rowBoundsWithCount" value="false" />
<property name="pageSizeZero" value="true" />
<property name="reasonable" value="false"/>
<property name="supportMethodsArguments" value="false" />
<property name="returnPageInfo" value="none" />
</plugin>
</plugins>
<environments>...</environments>
3.注意事项
- PageHelper.startPage(页码, 页大小,是否查询记录数); 每开一次执行一次,自动插入分页的SQL代码
- startPage只应用在最近一次查询,第二次无效
- 不要让流程语句干扰分页执行
- 获取分页信息
- 总记录数:page.getTotal();
- 总页数:page.getPages();
- 先查询,再获取总记录数,总页数,否则结果为0
Page<Object> page=PageHelper.startPage(1, 5,true);
List<Employee> list= mapper.getEmployees();
list.forEach(System.out::println);
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
5.反向工程
1.介绍
反向工程即是根据数据库中的表结构,表关系来生成mybatis相关的资源文件
(POJO类,映射器配置文件,动态代理接口,操作样本类)
注意;只用于单表操作,多表关系要自己配置
2.jar包和配置文件
在xml文件中的配置
- 数据库连接参数,地址,密码等
- 配置生成实体类的存放位置
- mapper映射文件存放位置
- 动态代理文件的位置
- 数据表
6.示例方法
1.增删改查方法
SqlSession session = getSession();
UserMapper mapper = session.getMapper(UserMapper.class); //获取mapper对象
- mapper.insert(user);
- mapper.deleteByPrimaryKey(9);
- mapper.updateByPrimaryKey(user);
- mapper.selectByPrimaryKey(2);
2.更新样式方法
根据样本中设置的条件执行更新
-
根据条件样本更新数据
mapper.updateByExample(实体对象, Bean样本); 该方法会清空其他属性的数据,相当于重新插入一个数据,不用提供id
-
根据条件锁定更新目标,根据属性是否有值更新字段
mapper.updateByExampleSelective(实体对象, Bean样本); 该方法直接在找到的数据上修改,不用提供id
-
直接更新数据
mapper.updateByPrimaryKey(user); 会清空同一数据的其他属性,需要提供id
mapper.updateByPrimaryKeySelective(user);不会清空同一数据的其他属性,需要提供id
3.查询数据
-
根据样本条件查询数据
mapper.selectByExample(ue); cri.andUserNameIn(list);//list中有的名字
-
根据Id查询
mapper.selectByPrimaryKey(5);
1.增删改查方法
SqlSession session = getSession();
UserMapper mapper = session.getMapper(UserMapper.class); //获取mapper对象
- mapper.insert(user);
- mapper.deleteByPrimaryKey(9);
- mapper.updateByPrimaryKey(user);
- mapper.selectByPrimaryKey(2);
2.更新样式方法
根据样本中设置的条件执行更新
-
根据条件样本更新数据
mapper.updateByExample(实体对象, Bean样本); 该方法会清空其他属性的数据,相当于重新插入一个数据,不用提供id
-
根据条件锁定更新目标,根据属性是否有值更新字段
mapper.updateByExampleSelective(实体对象, Bean样本); 该方法直接在找到的数据上修改,不用提供id
-
直接更新数据
mapper.updateByPrimaryKey(user); 会清空同一数据的其他属性,需要提供id
mapper.updateByPrimaryKeySelective(user);不会清空同一数据的其他属性,需要提供id
3.查询数据
-
根据样本条件查询数据
mapper.selectByExample(ue); cri.andUserNameIn(list);//list中有的名字
-
根据Id查询
mapper.selectByPrimaryKey(5);