MyBatis学习笔记(一), 内容包括:html
学习视频:尚硅谷雷丰阳老师MyBatis
https://www.bilibili.com/video/BV1bb411A7bDjava
链接数据库的方法:mysql
工具:JDBC→Dbutils(QueryRunner)→JdbcTemplategit
框架:总体解决方案github
Hibernate:全自动全映射ORM(Object Relation Mapping)框架,旨在消除SQLsql
缺点:数据库
SQL和Java编码分开,功能边界清晰,一个专一业务、一个专一数据。数组
MyBatis:半自动,轻量级的框架,sql语句提取到配置文件中编写安全
Maven 依赖:网络
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>
官方文档:https://mybatis.org/mybatis-3...
核心步骤:
建立tbl_employee表并插入三条数据:
CREATE TABLE tbl_employee( id INT(10) PRIMARY KEY AUTO_INCREMENT, last_name VARCHAR(255), gender CHAR(1), email VARCHAR(255) )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO tbl_employee VALUES (1,"zhansgan",0,"zhangsan@qq.com"), (2,"lisi",0,"lisi@163.com"), (3,"wangwu",1,"wangwu@126.com");
<dependencies> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <!--log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
public class Employee { private Integer id; private String lastName; //注意,该名称与数据库表的字段名不一致,查询会出现问题,能够用别名解决 private String email; private String gender; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Employee{" + "id=" + id + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", gender='" + gender + '\'' + '}'; } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--mybatis核心配置文件--> <configuration> <!--配置环境组,选择默认的环境id--> <environments default="development"> <!--配置单个环境并指定id为development--> <!--能够同时配置多个环境,可是只能选择一个使用--> <environment id="development"> <!--配置事务管理类型,JDBC--> <transactionManager type="JDBC"/> <!--配置链接池POOLED--> <dataSource type="POOLED"> <!--数据库链接池4个参数--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--配置映射,注册Mapper文件--> <mappers> <mapper resource="com/xiao/dao/EmployeeMapper.xml"/> </mappers> </configuration>
注意:
在xml配置文件中,url中的 &符号须要写成 &
在resources目录下表编写sql映射文件:EmployeeMapper.xml
sql语句的参数用#获取
<?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"> <!--namespace:名称空间,能够绑定一个Mapper接口--> <!--编写sql语句--> <!--id:惟一标识,若是有Mapper接口文件,须要对应其中的方法名--> <!--resultType:对应返回结果类型--> <!--#{id}:从传递过来的参数中取出id值--> <mapper namespace="com.xiao.dao.EmployeeMapper"> <select id="selectEmp" resultType="com.xiao.pojo.Employee"> select * from tbl_employee where id = #{id} </select> </mapper>
根据全局配置文件建立SqlSessionFactory对象,SqlSessionFactoryBulid→SqlSessionFactory→SqlSession。由sqlSession对象实例执行sql语句。
public class MyBatisTest { @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //由SqlSessionFactoryBuilder对象获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //由SqlSession工厂得到SqlSession对象,使用其进行增删改查 SqlSession sqlSession = sqlSessionFactory.openSession(); try { //sqlSession,直接执行已经映射的sql语句 //selectOne()中两个参数:sql的惟一标识(对应sql映射文件中的namespace.id)和执行sql须要的参数 Employee employee = sqlSession.selectOne("com.xiao.dao.EmployeeMapper.selectEmp", 1); System.out.println(employee); } finally { //一个sqlSession就是和数据库的一次会话,使用完以后须要关闭资源 sqlSession.close(); } } }
查询结果:
Employee{id=1, lastName='null', email='zhangsan@qq.com', gender='0'}
因为实体类的成员变量名和字段名不一致,所以lastName查询结果为null,能够在sql语句中取别名解决,也能够在sql映射文件中配置,后面会讲到。
修改sql语句:
select id,last_name lastName,gender,email from tbl_employee where id = #{id}
查询结果:
Employee{id=1, lastName='zhansgan', email='zhangsan@qq.com', gender='0'}
一种更有效的方法:建立接口文件com.xiao.dao.EmployeeMapper与sql映射文件绑定,在接口文件中定义方法,能够声明操做的返回值和参数。
注意:
public interface EmployeeMapper { //定义一个查询方法 Employee selectEmployeeById(Integer id); }
测试文件:
try { //得到接口文件的代理对象,执行方法 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); System.out.println(mapper.selectEmployeeById(1)); } finally { sqlSession.close(); }
build(InputStream in)
openSession()
,传入参数true能够设置为自动提交事务。getMapper()
,须要传入dao接口的class类型参数UserDao.class能够理解为数据库链接池对象,一旦建立了 SqlSessionFactory,就再也不须要它了,所以最佳做用域是方法做用域(也就是局部方法变量);
能够理解为数据库链接对象,一旦被建立就应该在应用的运行期间一直存在,所以 SqlSessionFactory 的最佳做用域是应用做用域。使用单例模式或者静态单例模式。
链接到链接池的一个请求,每一个线程都有它本身的 SqlSession 实例。SqlSession 的实例不是线程安全的,所以是不能被共享的,因此它的最佳的做用域是请求或方法做用域。
为了确保每次都能执行关闭操做,把关闭操做放到 finally 块中。
约束文件:
<?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">
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构以下,必须按顺序配置
configuration(配置)
environments(环境配置)
environment(环境变量)
经过properties属性能够实现引用配置文件,这些属性都是能够外部配置且可动态替换的。
<properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </properties> .... <!--使用${name}引用相应的值--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> ....
也能够引入外部的配置文件,例如编写一个外部的配置文件db.properties,而后在核心配置文件中引入:
#外部配置文件 driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username=root password=root
核心配置文件:
<!--在核心配置文件中引入外部配置文件,而后就可使用${name}引用--> <properties resource="db.properties"> </properties>
【注意事项】:若是一个属性在不仅一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
所以,经过方法参数传递的属性具备最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
经常使用设置:
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
fetchType
属性来覆盖该项的开关状态。默认是false类型别名可为 Java 类型设置一个缩写名字。 仅用于 XML 配置,意在下降冗余的全限定类名书写。别名不区分大小写
单个类起别名,<typeAlias>
标签:
<typeAliases> <!-- type:指定要起别名的类型全类名,默认是类名小写employee--> <!-- alias:指定新的别名--> <typeAlias type="com.xiao.pojo.Employee"/> </typeAliases>
<package>
标签,<typeAliases> <package name="com.xiao.pojo"/> </typeAliases>
@Alias
,则别名为其注解值:@Alias("Emp") public class Employee { ... }
常见的 Java 类型内建的类型别名。它们都是不区分大小写的,自定义的别名不要与其重复。
基本类型前面加下划线,引用类型首字母小写:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
数据库里的字段类型与Java的数据类型进行映射。
在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
MyBatis 容许在映射语句执行过程当中的某一点进行拦截调用。默认状况下,MyBatis 容许使用插件来拦截的方法调用包括:
MyBatis 能够配置成适应多种环境,能够配置多个环境,但每一个 SqlSessionFactory 实例只能选择一种环境。default参数指定使用某种环境。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
【注意事项】:
MyBatis 能够根据不一样的数据库厂商执行不一样的语句,考虑了移植性。
获得数据库厂商的标识(驱动getDatabaseProductName()),MyBatis就能根据数据库厂商标识来执行不一样的sql。
<databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider>
须要告诉 MyBatis 到哪里去找到 sql语句。可使用相对于类路径的资源引用,或彻底限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。
<mappers> <!-- 使用相对于类路径的资源引用 --> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <!-- 使用彻底限定资源定位符(URL) --> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <!-- 使用映射器接口实现类的彻底限定类名 --> <mapper class="org.mybatis.builder.AuthorMapper"/> <!-- 将包内的映射器接口实现所有注册为映射器 --> <package name="org.mybatis.builder"/> </mappers>
在dao接口文件中,用注解实现查询:
public interface EmployeeMapper { //定义一个查询方法 @Select("select * from tbl_employee where id = #{id}") Employee selectEmployeeById(Integer id); }
【一个小坑】:
映射文件指导MyBatis如何进行数据库增删改。
1) 首先在接口文件com.xiao.dao.EmployeeMapper中声明对应的增删改查方法,
同时能够设置返回值类型Integer、Long、Boolean,表示被影响的行数
public interface EmployeeMapper { //定义一个查询方法 @Select("select * from tbl_employee where id = #{id}") Employee selectEmployeeById(Integer id); //增长 void addEmp(Employee employee); //根据id删除 void deleteEmpById(Integer id); //修改 void updateEmp(Employee employee); }
2) 而后在sql映射文件EmployeeMapper.xml中编写相应的sql语句
<!--parameterType:参数类型,能够省略--> <insert id="addEmp" parameterType="employee"> insert into tbl_employee (last_name, gender, email) value (#{lastName},#{gender},#{email}) </insert> <delete id="deleteEmpById"> delete from tbl_employee where id = #{id} </delete> <update id="updateEmp"> update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id = #{id} </update>
3) 测试:
public void test2() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); //默认不自动提交事务,须要手动提交,或者构造方法传入true SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); //增 mapper.addEmp(new Employee(4,"林青霞","lingqingxia@163.com","1")); //删 mapper.deleteEmpById(2); //改 mapper.updateEmp(new Employee(1,"张柏芝","zhangbozhi@qq.com","1")); // 手动提交事务 sqlSession.commit(); } finally { sqlSession.close(); } }
注意事项:增删改须要提交事务
sqlSessionFactory.openSession():须要手动提交, sqlSession.commit();
sqlSessionFactory.openSession(true)是自动提交事务的
MySQL支持自增主键,在MyBatis中也是使用statement.getGeneratedKeys()获取自增主键的值。
用法,insert标签中:
<!--useGeneratedKeys="true":使用自增主键获取主键值策略--> <!--keyProperty="id":获取到的主键封装给JavaBean的id属性--> <insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id"> insert into tbl_employee (last_name, gender, email) value (#{lastName},#{gender},#{email}) </insert>
测试:employee.getId()方法能够获取到自增的主键值
Employee employee = new Employee(null,"Tom","tom@126.com","0"); mapper.addEmp(employee); System.out.println(employee.getId());
取参数的方式:#{参数名}
MyBatis不会作处理,传入id,sql语句中写 #{idabc} 也能取到参数
会被封装成一个map,#{}就是从map中获取指定key值的value。
key:param1,...paramN,或者参数的索引也能够
value:传入的参数值
若是按照以前的写法会报异常:
Employee employee = mapper.selectEmpByIdAndName(6, "Tom"); <select id="selectEmpByIdAndName" resultType="employee"> select * from tbl_employee where id = #{id} and last_name=#{lastName} </select>
BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
正确写法,可是通常不这么用:
select * from tbl_employee where id = #{param1} and last_name=#{param2}
常规用法:在接口方法中加注解@Param
:至关于key中保存的是@Param注解指定的值
Employee selectEmpByIdAndName(@Param("id") Integer id, @Param("lastName") String lastName);
//封装传入参数为Map,查询 Employee selectEmpByMap(Map<String,Object> map);
HashMap<String,Object> map = new HashMap<>(); //注意sql语句中#{id},#{lastName}与map中的键字段名称一一对应 map.put("id",6); map.put("lastName","Tom"); Employee employee = mapper.selectEmpByMap(map); System.out.println(employee);
select * from tbl_employee where id = #{id} and last_name=#{lastName}
Employee getEmp(@Param("id") Integer id, String lastName)
取值:id ==> #{id}或者#{param1},lastName ==> #{param2}
Employee getEmp(Integer id, @Param("e") Employee emp)
取值:id ==> #{param1},lastName ==> #{e.lastName}或者 #{param2.lastName}
key:collection,list,array
Employee getEmp(List<Integer> ids)
取值:取出第一个id的值 #{list[0]}
总结:参数多时封装成map,结合@Param指定封装时使用的key,#{key}取出map中的值
#{}
:是以预编译的形式,即占位符,将参数设置到sql语句中,相似于JDBC中的PreparedStament,防止sql注入
${}
:取出的值直接拼装在sql语句中,会有安全问题。
原生JDBC中不支持占位符的地方就能够用${取值},好比表名、排序字段等
select * from ${year}_salary where ...
select * from tbl_employee order by ${name}
select语句中resultType写的是集合中元素的类型
//查询全部,返回一个集合 List<Employee> selectAll();
<select id="selectAll" resultType="employee"> select * from tbl_employee </select>
测试:
List<Employee> employees = mapper.selectAll(); for (Employee employee : employees) { System.out.println(employee); }
结果:
Employee{id=1, lastName='张曼玉', email='zhangmanyu@163.com', gender='1'} Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'} Employee{id=5, lastName='林青霞', email='lingqingxia@163.com', gender='1'} Employee{id=6, lastName='Tom', email='tom@126.com', gender='0'}
若是想把一条记录的返回值类型封装为map,key值为字段名,value值。则指定resultType为map,查询到的结果会将指定的列映射到Map 的键上,结果映射到值上。
//返回一条记录的map,key是列名,值就是对应的值 Map<String,Object> selectEmpByIdToMap(Integer id);
<!--查询并返回一条记录的map--> <select id="selectEmpByIdToMap" resultType="map"> select * from tbl_employee where id = #{id} </select>
测试:
Map<String, Object> map = mapper.selectEmpByIdToMap(1); System.out.println(map);
结果:
{gender=1, last_name=张曼玉, id=1, email=zhangmanyu@163.com}
若是想封装多条记录到map中,key是主键值,value是JavaBean对象,则在接口方法上使用@MapKey
注解指定key的属性; resultType依然为map中的value类型
//返回多条记录封装到map中,key是主键值,value是JavaBean对象 //@MapKey;指定返回的map的key @MapKey("id") Map<Integer,Employee> selectAllToMap();
<!-- 返回多条记录封装到map中,key是主键值,value是JavaBean对象--> <select id="selectAllToMap" resultType="employee"> select * from tbl_employee </select>
测试:
Map<Integer, Employee> map = mapper.selectAllToMap(); System.out.println(map);
结果:
{1=Employee{id=1, lastName='张曼玉', email='zhangmanyu@163.com', gender='1'}, 3=Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'}, 5=Employee{id=5, lastName='林青霞', email='lingqingxia@163.com', gender='1'}, 6=Employee{id=6, lastName='Tom', email='tom@126.com', gender='0'}}
而指定 resultType
属性为实体类时,查询到的结果会将指定的列映射到类的成员变量名上,结果映射到成员变量值上。
【注意】:实体类中的属性名和数据库中的字段名保持一致。不然结果值会为null。
若是列名和属性名不能匹配上,且不知足驼峰命名自动映射,能够在 SELECT 语句中设置列别名,也能够显式配置 ResultMap
若是列名和属性名不能匹配上,在xml映射文件中显式配置 ResultMap
标签
<!--自定义结果集规则--> <!--type:自定义规则的Java类型--> <!--id:惟一id,用于引用--> <resultMap id="MyEmp" type="employee"> <!--用id标签订义主键底层会有优化,普通列用result标签--> <!--column:指定哪一列--> <!--property:指定对应的JavaBean属性--> <!--不指定的列会自动封装--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </resultMap> <!--用resultMap取代resultType,值为自命名的id--> <select id="selectEmp" resultMap="MyEmp"> select * from tbl_employee where id = #{id} </select>
处理多表查询。
MyBatis 有两种不一样的方式加载关联:
两张表,员工表tbl_employee和部门表tbl_dept,员工表中设置外键部门id,指向部门表的主键
在以前的基础上新建了部门表,并在员工表中设置外键,插入数据:
CREATE TABLE tbl_dept( id INT(10) PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(255) )ENGINE=INNODB DEFAULT CHARSET=UTF8; ALTER TABLE tbl_employee ADD COLUMN d_id INT(10); ALTER TABLE tbl_employee ADD CONSTRAINT fk_emp_dept FOREIGN KEY(d_id) REFERENCES tbl_dept(id);
员工表:
部门表:
Employee实体类:
public class Employee { private Integer id; private String lastName; private String email; private String gender; private Dept dept; //getter/setter... }
Dept实体类:
public class Dept { private Integer id; private String departmentName; //getter/setter... }
在接口文件中声明一个根据员工id查询员工信息的方法,由于员工信息中一个成员是部门对象,按照以前的映射关系,没法查询部门信息。
public interface EmployeeMapper { //根据id查询员工的信息及其部门信息 Employee getEmpAndDept(Integer id); }
采用级联属性来封装结果:
employee中有一个成员是dept,由dept.id和dept.departmentName则能够获取到部门的id和部门名称
<!--联合查询,级联属性封装结果集--> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <result column="did" property="dept.id"/> <result column="dept_name" property="dept.departmentName"/> </resultMap> <select id="getEmpAndDept" resultMap="MyDifEmp "> <!--给列起别名--> select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = #{id} </select>
测试:
Employee employee = mapper.getEmpAndDept(1); System.out.println(employee);
结果:重点是执行的sql语句
DEBUG 06-12 20:16:45,106 ==> Preparing: select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:16:45,213 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:16:45,253 <== Total: 1 (BaseJdbcLogger.java:143) Employee{id=1, lastName='张曼玉', email='zhangmanyu@163.com', gender='1', dept=Dept{id=1, departmentName='开发部'}}
关联(association)元素能够处理多表查询。
MyBatis 有两种不一样的方式加载关联:
查询结果本质上是Employee类,其中的成员Dept是对象,须要进行关联association,用javaType获取属性的类型。两种方式结果相同。
<!--查询结果本质上是Employee类--> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <!--对查询结果进行映射--> <!--数据库中的字段取了别名后用别名--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <!-- dept成员是一个对象,须要用association指定联合的JavaBean对象--> <!-- property="dept":指定哪一个属性是联合的对象--> <!-- javaType:指定这个属性对象的类型,不能省略--> <association property="dept" javaType="com.xiao.pojo.Dept"> <id column="did" property="id"/> <id column="dept_name" property="departmentName"/> </association> </resultMap> <select id="getEmpAndDept" resultMap="MyDifEmp"> <!--给列起别名--> select e.id id, e.last_name last_name,e.gender gender,e.email email, d.id did,d.dept_name dept_name from tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = #{id} </select>
思路:
<!--association分步查询--> <!--第一步,先查询员工信息--> <!--第二步,根据查询到的员工信息的did,寻找对应的部门信息--> <select id="getEmpAndDept" resultMap="MyDifEmp"> <!--第一步,先查询员工信息--> select id, last_name, gender,email,d_id from tbl_employee where id = #{id} </select> <resultMap id="MyDifEmp" type="com.xiao.pojo.Employee"> <!--简单的属性直接写--> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="email" property="email"/> <!--复杂的属性,须要单独处理--> <!--select:调用指定的方法查出结果--> <!--column:将哪一列的值传给这个方法--> <!--property:查出的结果封装给property指定的属性--> <association property="dept" column="d_id" javaType="com.xiao.pojo.Dept" select="getDept"/> </resultMap> <!--第二步,根据查询到的员工信息中的d_id,查询对应的部门信息--> <!--能够写到部门的sql映射文件中,这里简略了--> <select id="getDept" resultType="com.xiao.pojo.Dept"> select id,dept_name departmentName from tbl_dept where id = #{d_id} </select>
association标签中的属性:
执行流程:使用select指定的方法,用传入的column指定的这列参数的值,查出对象,并封装给property属性
结果:有两条sql语句
DEBUG 06-12 20:04:49,831 ==> Preparing: select id, last_name, gender,email,d_id from tbl_employee where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:49,945 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,006 ====> Preparing: select id,dept_name departmentName from tbl_dept where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,008 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,015 <==== Total: 1 (BaseJdbcLogger.java:143) DEBUG 06-12 20:04:50,017 <== Total: 1 (BaseJdbcLogger.java:143) Employee{id=1, lastName='张曼玉', email='zhangmanyu@163.com', gender='1', dept=Dept{id=1, departmentName='开发部'}}
延迟加载(懒加载、按需加载),须要在全局配置文件中开启
fetchType
属性来覆盖该项的开关状态。默认是false<settings> <!--开启懒加载--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings>
测试中只使用查询到的员工信息,不使用员工信息,则只会发出一条sql语句,若是不开启延迟加载,则老是会发出两条sql语句:
Employee employee = mapper.getEmpAndDept(1); System.out.println(employee.getEmail());
结果,只发出了第一步的sql语句:
DEBUG 06-12 20:31:55,182 ==> Preparing: select id, last_name, gender,email,d_id from tbl_employee where id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 20:31:55,290 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 20:31:55,516 <== Total: 1 (BaseJdbcLogger.java:143) zhangmanyu@163.com
查询部门,并讲部门对应的全部员工信息查询出来
public class Employee { private Integer id; private String lastName; private String email; private String gender; //getter/setter... }
Dept实体类:
public class Dept { private Integer id; private String departmentName; private List<Employee> emps; //getter/setter... }
DeptMapper接口文件:
public interface DeptMapper { Dept selectDeptById(Integer id); }
使用关联查询,根据Dept的id查询Dept信息,其中Dept中有一个成员是emps,即员工的集合,须要用到collection标签进行结果映射。
集合标签:collection
sql映射文件:
<?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 namespace="com.xiao.dao.DeptMapper"> <!--嵌套结果查询--> <select id="selectDeptById" resultMap="MyDept"> select d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name, e.email email, e.gender gender from tbl_dept d left join tbl_employee e on d.id = e.d_id where d.id = #{id} </select> <resultMap id="MyDept" type="com.xiao.pojo.Dept"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> <!--复杂的属性,单独处理,集合:collection,property指定哪一个属性是集合,用ofType获取集合中的泛型信息--> <collection property="emps" ofType="com.xiao.pojo.Employee"> <result column="eid" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap> </mapper>
测试:
Dept dept = mapper.selectDeptById(1); System.out.println(dept);
查询结果,一个部门中有两个员工信息:
DEBUG 06-12 21:24:02,595 ==> Preparing: select d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name, e.email email, e.gender gender from tbl_dept d left join tbl_employee e on d.id = e.d_id where d.id = ? (BaseJdbcLogger.java:143) DEBUG 06-12 21:24:02,675 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143) DEBUG 06-12 21:24:02,721 <== Total: 2 (BaseJdbcLogger.java:143) Dept{id=1, departmentName='开发部', emps=[Employee{id=1, lastName='张曼玉', email='zhangmanyu@163.com', gender='1'}, Employee{id=3, lastName='wangwu', email='wangwu@126.com', gender='0'}]}
思路与多对一中的思路类型:
<!--分步查询--> <select id="selectDeptById" resultMap="MyDept"> select id, dept_name from tbl_dept where id = #{id} </select> <resultMap id="MyDept" type="com.xiao.pojo.Dept"> <id column="id" property="id"/> <result column="dept_name" property="departmentName"/> <collection property="emps" ofType="com.xiao.pojo.Employee" select="getEmp" column="id"/> </resultMap> <select id="getEmp" resultType="com.xiao.pojo.Employee"> select id,last_name lastName,email,gender from tbl_employee where d_id = #{id} </select>
将多列的值封装成map传递:column={key1=column1,key2=column2...},使用时则用#{key}获取
开启了全局懒加载后,特定关联关系中可经过设置 fetchType
属性来覆盖该项的开关状态
鉴别器discriminator
相似于 Java 语言中的 switch 语句,能够根据不一样的条件封装不一样的结果集。须要指定 column 和 javaType 属性。
<discriminator javaType="string" column="gender"> <case value="0" resultType="com.xiao.pojo.Employee"> .... </case> <case value="0" resultType="com.xiao.pojo.Employee"> .... </case> </discriminator>