MyBatis映射文件 1 增删改查概述 1.1 Mapper接口定义
在Mapper接口中定义增删改查方法的参数和返回值类型,
1 2 3 4 5 6 public interface EmployeeMapper { Employee getEmployeeById (Integer id) ; void addEmployee (Employee employee) ; void updateEmployee (Employee employee) ; void deleteEmployeeById (Integer id) ; }
1.2 Mapper配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <mapper namespace ="com.lnhoo.dao.EmployeeMapper" > <select id ="getEmployeeById" resultType ="com.lnhoo.Employee" > select * from employees where id = #{id} </select > <insert id ="addEmployee" parameterType ="com.lnhoo.Employee" > insert into employees (id, last_name, gender, email) values (#{id}, #{lastName}, #{gender}, #{email}) </insert > <update id ="updateEmployee" parameterType ="com.lnhoo.Employee" > update employees set last_name=#{lastName}, gender=#{gender}, email=#{email} where id=#{id} </update > <delete id ="deleteEmployeeById" parameterType ="int" > delete from employees where id=#{id} </delete > </mapper >
在Mapper配置文件中将接口中的方法和sql语句绑定
mapper标签的namespace属性填上接口的全类名
增(insert)、删(delete)、改(update)、查(select)标签的id属性填上接口中的方法名
参数提供的字段、sql语句执行的结果以“#{名字}”的形式捕获
1.3 执行sql 1 2 3 4 5 6 7 8 9 10 @Test public void testModify () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); try (SqlSession session = sqlSessionFactory.openSession()) { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); Employee jack = new Employee(0 , "jack" , '1' , "xxx@163.com" ); employeeMapper.addEmployee(jack); session.commit(); } }
先获取到SqlSessionFactory对象,然后调用openSession方法开启一次会话,从会话中获取MyBatis自动创建的动态代理对象,之后就可以调用事先在EmployeeMapper接口中定义的方法实现对数据库的增删改查
调用openSession方法如果不提供参数,此时的会话将不自动提交更改,需要在合适的时候手动调用commit方法提交更改;如果调用重载方法,则可以提供一个布尔类型的参数,表示是否自动commit
1.4 获取增、删、改执行结果
MyBatis支持将接口中增、删、改方法的返回值类型定义成void、Integer、Long、Boolean,
1 2 3 4 5 6 public interface EmployeeMapper { Employee getEmployeeById (Integer id) ; Integer addEmployee (Employee employee) ; Long updateEmployee (Employee employee) ; Boolean deleteEmployeeById (Integer id) ; }
返回值类型为整数时,表示sql语句执行影响的记录数
如果返回值类型是布尔值,表示sql语句执行是否对数据库表内容造成影响
1.5 获取自增主键的值
useGeneratedKeys=”true”:使用自增主键获取主键值的策略
keyProperty:指定对应的主键属性,也就是MyBatis获取到主键值后,将这个值封装给Java Bean的哪个属性
1 2 3 <insert id ="addEmployee" parameterType ="com.lnhoo.Employee" useGeneratedKeys ="true" keyProperty ="id" > insert into employees (id, last_name, gender, email) values (#{id}, #{lastName}, #{gender}, #{email}) </insert >
2 参数处理 2.1 单个参数
MyBatis不作特殊处理,直接#{参数名}取出参数值
1 2 3 4 Employee getEmployeeById (Integer id) ;<select id="getEmployeeById" resultType="com.lnhoo.Employee" > select * from employees where id = #{id} </select>
2.2 多个参数 2.2.1 通过索引取出参数值
多个参数会被封装成一个Map,索引为:param1、param2、…、paramN,
1 2 3 4 Employee getEmployeeByIdAndLastName (Integer id, String lastName) ;<select id="getEmployeeByIdAndLastName" resultType="com.lnhoo.Employee" > select * from employees where id=#{param1} and last_name=#{param2} </select>
2.2.2 命名参数
1 2 3 4 Employee getEmployeeByIdAndLastName (@Param(value = "id") Integer id, @Param(value = "lastName") String lastName) ;<select id="getEmployeeByIdAndLastName" resultType="com.lnhoo.Employee" > select * from employees where id=#{id} and last_name=#{lastName} </select>
2.2.3 POJO
如果多个参数正好是业务逻辑的数据模型,就可以直接传入POJO,#{属性名}取出传入的POJO的属性值,
1 2 3 4 Integer addEmployee (Employee employee) ;<insert id="addEmployee" parameterType="com.lnhoo.Employee" > insert into employees (id, last_name, gender, email) values (#{id}, #{lastName}, #{gender}, #{email}) </insert>
2.2.4 Map
如果多个参数不是业务逻辑的数据模型,没有对应的POJO,也可以传入Map,#{key}取出参数值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Employee getEmployeeByMap (Map<String, Object> map) ;<select id="getEmployeeByMap" resultType="com.lnhoo.Employee" > select * from employees where id=#{id} and last_name=#{lastName}; </select> @Test public void testSelect () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); try (SqlSession session = sqlSessionFactory.openSession()) { EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); Map queryMap = new HashMap<String, Object>(); queryMap.put("id" , 1 ); queryMap.put("lastName" , "tom" ); Employee employee = employeeMapper.getEmployeeByMap(queryMap); System.out.println(employee); } }
2.2.5 DTO
如果多个参数不是业务模型中的数据,但是经常使用,可以考虑定义DTO(Data Transfer Object 数据传输对象)
2.2.6 #{}和${}取值的区别
#{}是以预编译的形式将参数设置到sql语句中,可以防止sql注入
${}取出的值直接拼装在sql语句中,会有安全问题
大多数情况下,取参数的值应该使用#{}的方式,在原生JDBC不支持占位符的地方就可以使用${}进行取值
2.2.7 #{}取值扩展用法 2.2.7.1 jdbcType
当提供的参数为null的时候,有些数据库不能识别MyBatis对null的默认处理,比如Oracle就会报错
默认情况下,MyBatis运行时设置项jdbcTypeForNull=OTHER,此时MyBatis对所有null都映射的是原生JDBC的OTHER类型,而oracle不认识
可以在#{}取参数值的时候指定jdbcType,
1 2 3 <select id ="getEmployeeByMap" resultType ="com.lnhoo.Employee" > select * from employees where id=#{id} and last_name=#{lastName, jdbcType=NULL}; </select >
也可以在全局配置文件中修改MyBatis运行时设置项,
1 2 3 <settings > <setting name ="jdbcTypeForNull" value ="NULL" /> </settings >
3 select 3.1 查询返回list
在Mapper接口定义一方法,表示根据lastName查询数据表中符合条件的记录,
1 List<Employee> getEmployeeByLastName (String lastName) ;
Mapper xml配置文件中将sql语句和接口方法绑定,
1 2 3 <select id ="getEmployeeByLastName" resultType ="com.lnhoo.Employee" > select * from employees where last_name like "%${lastName}%" </select >
这里虽然查询返回的结果集被封装成List集合,但是select标签的resultType属性要填集合中对象的类型,不能写成集合的类型
3.2 resultMap 3.2.1 自定义结果集映射规则
可以使用resultMap标签自定义结果集的映射规则,
1 2 3 4 5 6 <resultMap id ="Employee" type ="com.lnhoo.Employee" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="email" property ="email" > </result > <result column ="gender" property ="gender" > </result > </resultMap >
resultMap标签的id属性是resultMap定义的唯一标识,方便后续引用,type是Java Bean的全类名
resultMap标签内部的id标签用于定义数据库表主键和Java Bean中属性的映射关系,此处也可以使用result标签替代
result标签表明数据库表中的列和Java Bean属性的映射关系
在select标签中引用resultMap来使用自定义的映射规则,
1 2 3 <select id ="getEmployeeById" resultMap ="Employee" > select * from employees where id=#{id} </select >
3.2.2 关联查询
1 2 3 4 5 6 7 8 9 10 11 12 13 create table departments ( id int (11 ) primary key auto_increment, dept_name varchar (255 ) ) insert into departments values (0 , "Human Resource");insert into departments values (0 , "Infomation Technology")select * from departments;id| dept_name | 1 | Human Resource | 2 | Infomation Technology|
1 2 3 4 5 6 7 8 9 alter table employees add column dept_id int (11 );describe employees Field | Type | Null | Key| Default | Extra | id | int (11 ) | NO | PRI| | auto_increment| last_name| varchar (255 )| YES | | | | gender | char (1 ) | YES | | | | email | varchar (255 )| YES | | | | dept_id | int (11 ) | YES | | | |
添加部门Department类,修改Employee类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public class Department { private int id; private String name; public Department () { } public Department (int id, String name) { this .id = id; this .name = name; } public void setId (int id) { this .id = id; } public void setName (String name) { this .name = name; } @Override public String toString () { return "Department{" + "id=" + id + ", name='" + name + '\'' + '}' ; } } public class Employee { private Integer id; private String lastName; private char gender; private String email; private Department dept; public Employee () { } public Employee (Integer id, String lastName, char gender, String email) { this .id = id; this .lastName = lastName; this .gender = gender; this .email = email; } public void setId (Integer id) { this .id = id; } public void setLastName (String lastName) { this .lastName = lastName; } public void setGender (char gender) { this .gender = gender; } public void setEmail (String email) { this .email = email; } public void setDept (Department dept) { this .dept = dept; } @Override public String toString () { return "Employee{" + "id=" + id + ", lastName='" + lastName + '\'' + ", gender=" + gender + ", email='" + email + '\'' + ", dept=" + dept + '}' ; } }
在Mapper xml配置文件中将接口方法和连接查询的sql语句绑定,
1 2 3 <select id ="getEmployeeById" resultMap ="Employee" > select e.id id, last_name, gender, email, d.id did, dept_name from employees e, departments d where e.dept_id=d.id and e.id=#{id} </select >
自定义查询结果集字段和Java Bean属性之间的映射关系,
1 2 3 4 5 6 7 8 <resultMap id ="Employee" type ="com.lnhoo.Employee" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="email" property ="email" > </result > <result column ="gender" property ="gender" > </result > <result column ="did" property ="dept.id" > </result > <result column ="dept_name" property ="dept.name" > </result > </resultMap >
使用”.”操作符取出对象类型成员的属性,比如”dept.id”、”dept.name”
3.2.3 定义关联对象的封装规则
在resultMap中使用association标签定义关联对象的封装规则,
1 2 3 4 5 6 7 8 9 10 <resultMap id ="Employee" type ="com.lnhoo.Employee" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="email" property ="email" > </result > <result column ="gender" property ="gender" > </result > <association property ="dept" javaType ="com.lnhoo.Department" > <id column ="did" property ="id" > </id > <result column ="dept_name" property ="name" > </result > </association > </resultMap >
association标签的property属性填成员变量名称,javaType填该成员的全类名
3.2.4 分步查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id ="MyEmpByStep" type ="com.lnhoo.Employee" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="gender" property ="gender" > </result > <result column ="email" property ="email" > </result > <association property ="dept" select ="com.lnhoo.dao.DepartmentMapper.getDepartmentById" column ="dept_id" > <id column ="id" property ="id" > </id > <result column ="dept_name" property ="name" > </result > </association > </resultMap > <select id ="getEmpByStep" resultMap ="MyEmpByStep" > select * from employees where id=#{id}; </select >
association标签的select属性可支持分步查询,参数由column属性提供
3.2.5 延迟加载
设置
功能
默认值
lazyLoadingEnabled
延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态
false
aggressiveLazyLoading
开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。
false(在 3.4.1 及之前的版本中默认为 true)
所以要配置延迟加载,只需要在全局配置文件中设置两个开关,
1 2 3 4 5 <settings > ...... <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings >
association、collection标签均有fetchType属性,可能值为“lazy”(延迟加载)、“eager”(立即加载)
3.2.6 定义关联集合封装规则
通常员工和部门是多对一的关系,即一个部门拥有多个员工,一个员工只属于一个部门,
1 2 3 4 5 public class Department { private int id; private String name; private List<Employee> employeeList; ............
假设Department对象包含一个员工列表employeeList
使用collection标签定义关联集合封装规则,
1 2 3 4 5 6 7 8 9 10 <resultMap id ="Department" type ="com.lnhoo.Department" > <id column ="id" property ="id" > </id > <result column ="dept_name" property ="name" > </result > <collection property ="employeeList" ofType ="com.lnhoo.Employee" > <id column ="eid" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="gender" property ="gender" > </result > <result column ="email" property ="email" > </result > </collection > </resultMap >
1 2 3 public interface DepartmentMapper { Department getDepartmentById (Integer id) ; }
1 2 3 4 5 <select id ="getDepartmentById" resultMap ="Department" > select d.id id, dept_name, e.id eid, last_name, gender, email, dept_id from departments d left join employees e on d.id=e.dept_id where d.id=#{id} </select >
3.2.7 关联集合分步查询
假设要根据dept_id查询某个部门,Department对象有一employeeList字段,实现思路可以是:先根据部门id查询departments表得到部门名称,然后再根据部门id查询employees表得到属于该部门的所有员工填充到Department对象的employeeList属性中
父查询:根据部门id查询对应的部门,
1 2 3 <select id ="getDepartmentById" resultMap ="Department" > select * from departments where id=#{id} </select >
1 2 3 <select id ="getEmployeeByDeptId" resultType ="com.lnhoo.Employee" > select * from employees where dept_id=#{id} </select >
resultMap使用collection标签关联父、子查询,
1 2 3 4 5 6 7 8 9 10 <resultMap id ="MyEmpByStep" type ="com.lnhoo.Employee" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="gender" property ="gender" > </result > <result column ="email" property ="email" > </result > <association property ="dept" select ="com.lnhoo.dao.DepartmentMapper.getDepartmentById" column ="dept_id" > <id column ="id" property ="id" > </id > <result column ="dept_name" property ="name" > </result > </association > </resultMap >
3.2.8 分步查询传递多列值
association、collection标签分步查询如果要为select语句传递多个参数,需要封装成一个map,
1 column="{key1: column1, key2: column2}"
3.2.9 鉴别器
discriminator可以根据查询结果集的某个列决定封装的对象
假如有个需求,要求:
(1)只有女生才查询所属的部门
(2)男生的email被赋值成last_name
可以使用鉴别器实现如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <resultMap id ="MyEmpByStep" type ="com.lnhoo.Employee" > <discriminator javaType ="string" column ="gender" > <case value ="0" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="gender" property ="gender" > </result > <result column ="email" property ="email" > </result > <association property ="dept" select ="com.lnhoo.dao.DepartmentMapper.getDepartmentById" column ="dept_id" > <id column ="id" property ="id" > </id > <result column ="dept_name" property ="name" > </result > </association > </case > <case value ="1" > <id column ="id" property ="id" > </id > <result column ="last_name" property ="lastName" > </result > <result column ="gender" property ="gender" > </result > <result column ="last_name" property ="email" > </result > </case > </discriminator > </resultMap >