Mybatis入门
Mybatis的介绍
MyBatis 本是 apache 的一个开源项目 iBatis, 2010年这个项目由apache software foundation 迁移到了 google code,并且改名为MyBatis 。2013年11月迁移到 Github。
MyBatis是一个优秀的持久层框架,它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代码。
Mybatis 通过 xml 或注解的方式将要执行的各种 statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和 statement 中的sql进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射成 java 对象并返回。
使用jdbc编程问题总结
创建mysql数据库
- 创建数据库
- 将sql脚本文件导入到数据库中
创建工程
开发环境
IDE: Intellij IDEA
JDK: 1.9
- 创建一个Java工程
按下图进行创建
- 导入需要的数据库驱动
在 file -> project Setting -> Moudules -> Dependencies里,添加jar文件。
jdbc编程步骤
- 加载数据库驱动
- 创建并获取数据库链接
- 创建jdbc statement对象
- 设置sql语句
- 设置sql语句中的参数(使用preparedStatement)
- 通过statement执行sql并获取结果
- 对sql执行结果进行解析处理
- 释放资源(resultSet、preparedstatement、connection)
jdbc程序
1 | public static void main(String[] args) { |
上边使用jdbc的原始方法(未经封装)实现了查询数据库表记录的操作。
jdbc问题总结如下
- 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
- Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
Mybatis架构
mybatis配置SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
Mybatis入门程序
mybatis下载
mybaits的代码由github.com管理
下载地址:https://github.com/mybatis/mybatis-3/releases
mybatis-3.2.7.jar ——-》 mybatis的核心包
lib文件夹 ——-》 mybatis的依赖包所在
mybatis-3.2.7.pdf ——-》 mybatis使用手册
业务需求
- 使用MyBatis实现以下功能:
- 根据用户id查询一个用户
- 根据用户名称模糊查询用户列表
- 添加用户
- 更新用户
- 删除用户
环境搭建
创建Java工程
这个前面讲过,直接省略。。。
加入jar包
加入mybatis核心包、依赖包、数据驱动包。
- mybatis核心包
- mybatis依赖包
- 数据库驱动包
效果:
加入配置文件
在src文件夹下,加入log4j.properties和SqlMapConfig.xml配置文件
log4j.properties
在 src 下创建log4j.properties如下:
1 | # Global logging configuration |
注意:mybatis默认使用log4j作为输出日志信息。
SqlMapConfig.XML
在 src 下创建SqlMapConfig.xml,如下:
1 |
|
注意:SqlMapConfig.xml是mybatis核心配置文件,配置文件内容为数据源、事务管理。
效果:
创建pojo
pojo类作为mybatis进行sql映射使用,po类通常与数据库表对应,
数据库如下所示:
1 | DROP TABLE IF EXISTS `user`; |
User.java如下:
1 | package com.share.mybatis.pojo; |
sql映射文件
User.xml:
1 |
|
加载映射文件
mybatis框架需要加载Mapper.xml映射文件
将users.xml添加在SqlMapConfig.xml,如下:
1 | <!-- Mapper位置 --> |
根据id查询用户
使用的sql:SELECT * FROM 'user' WHERE id = 1
映射文件
在user.xml中添加select标签,编写sql:
1 |
|
测试程序:
测试程序步骤:
- 创建SqlSessionFactoryBuilder对象
- 加载SqlMapConfig.xml配置文件
- 创建SqlSessionFactory对象
- 创建SqlSession对象
- 执行SqlSession对象执行查询,获取结果User
- 打印结果
- 释放资源
MybatisTest编写测试程序如下:
1 | public class MybatisTest { |
效果
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1077072774.
DEBUG [main] - Setting autocommit to false on JDBC
Connection [com.mysql.cj.jdbc.ConnectionImpl@4032d386]
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=王五, sex=2, birthday=null, address=null]
实现根据用户名模糊查询用户
查询sql:SELECT * FROM 'user' WHERE username LIKE '%王%'
方法一
映射文件
在User.xml配置文件中添加如下内容:
1 | <!-- 如果返回多个结果,mybatis会自动把返回的结果放在list容器中 --> |
测试程序
MybatisTest中添加测试方法如下:
1 |
|
结果
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 554868511.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@21129f1f]
DEBUG [main] - ==> Preparing: select * from user where username like '%%五%'
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 2
User [id=1, username=王五, sex=2, birthday=null, address=null]
User [id=26, username=王五, sex=null, birthday=null, address=null]
方法二
映射文件:
在User.xml配置文件中添加如下内容:
1 | <!-- 如果传入的参数是简单数据类型,${}里面必须写value --> |
测试程序:
MybatisTest中添加测试方法如下:
1 |
|
效果
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 554868511.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@21129f1f]
DEBUG [main] - ==> Preparing: select * from user where username like '%%五%'
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 2
User [id=1, username=王五, sex=2, birthday=null, address=null]
User [id=26, username=王五, sex=null, birthday=null, address=null]
小结
#{}和${}
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
parameterType和resultType
parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中
selectOne和selectList
selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:
1 | org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3 |
selectList可以查询一条或多条记录。
实现添加用户
使用的sql:INSERT INTO 'user' (username,birthday,sex,address) VALUES ('黄忠','2016-07-26','1','三国')
映射文件:
在User.xml配置文件中添加如下内容:
1 | <!-- 保存用户 --> |
测试程序
MybatisTest中添加测试方法如下:
1 |
|
效果
1 | DEBUG [main] - Created connection 1550228904. |
如上所示,保存成功,但是id=0,需要解决id返回不正常的问题。
mysql自增主键返回
查询id的sqlSELECT LAST_INSERT_ID()
通过修改User.xml映射文件,可以将mysql自增主键返回:
如下添加selectKey 标签
1 | <!-- 保存用户 --> |
LAST_INSERT_ID() : 是mysql的函数,返回auto_increment自增列新记录id值。
效果:
1 | DEBUG [main] - Created connection 1550228904. |
Mysql使用 uuid实现主键
需要增加通过select uuid()得到uuid值
1 | <!-- 保存用户 --> |
注意: 这里使用的order是“BEFORE”
修改用户
根据用户id修改用户名
使用的sql:UPDATE 'user' SET username = '赵云' WHERE id = 26
映射文件
在User.xml配置文件中添加如下内容:
1 | <!-- 更新用户 --> |
测试程序
MybatisTest中添加测试方法如下:
1 |
|
效果
1 | DEBUG [main] - Opening JDBC Connection |
删除用户
根据用户id删除用户
使用的sqlDELETE FROM 'user' WHERE id = 47
映射文件:
在User.xml配置文件中添加如下内容:
1 | <!-- 删除用户 --> |
测试程序:
MybatisTest中添加测试方法如下:
1 |
|
效果
1 | DEBUG [main] - Opening JDBC Connection |
Mybatis解决jdbc编程的问题
- 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。 - Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。 - 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。 - 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
mybatis与hibernate不同
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
Dao开发方法
使用MyBatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper动态代理开发方法。
需求
- 使用MyBatis开发DAO实现以下的功能:
- 根据用户id查询一个用户信息
- 根据用户名称模糊查询用户信息列表
- 添加用户信息
SqlSession的使用范围
SqlSession中封装了对数据库的操作,如:查询、插入、更新、删除等。
SqlSession通过SqlSessionFactory创建。
SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory创建的。所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
SqlSession
SqlSession是一个面向用户的接口,sqlSession中定义了数据库操作方法。
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。如下:
1 | SqlSession session = sqlSessionFactory.openSession(); |
原始Dao开发方式
原始Dao开发方法需要程序员编写Dao接口和Dao实现类。
映射文件
编写映射文件如下:(也可以使用入门程序完成的映射文件)
1 |
|
Dao接口
先进行DAO的接口开发,编码如下:
1 | public interface UserDao { |
Dao实现类
编写的Dao实现类如下:
1 | public class UserDaoImpl implements UserDao { |
Dao测试
创建一个JUnit的测试类,对UserDao进行测试,测试代码如下:
1 | public class UserDaoTest { |
问题
原始Dao开发中存在以下问题:
- Dao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
- 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
Mapper动态代理方式
开发规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的类路径相同。
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mapper.xml(映射文件)
定义mapper映射文件UserMapper.xml
将UserMapper.xml放在src下sqlmap目录下
UserMapper.xml配置文件内容:
1 |
|
UserMapper(接口文件)
创建UserMapper接口代码如下:
1 | public interface UserMapper { |
加载UserMapper.xml文件
修改SqlMapConfig.xml文件,添加以下所示的内容:
1 | <!-- 加载映射文件 --> |
测试
编写的测试方法如下:
1 | public class UserMapperTest { |
小结
selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
SqlMapConfig.xml配置文件
配置内容
SqlMapConfig.xml中配置的内容和顺序如下:
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- mappers(映射器)
properties(属性)
SqlMapConfig.xml可以引用java属性文件中的配置信息如下:
在config下定义jdbc.properties文件,如下所示:
jdbc.properties配置文件内容如下:
1 | jdbc.driver=com.mysql.jdbc.Driver |
SqlMapConfig.xml引用如下:
1 |
|
注意: MyBatis 将按照下面的顺序来加载属性:
- 在 properties 元素体内定义的属性首先被读取。
- 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
typeAliases(类型别名)
mybatis支持别名:
别名 | 映射的类型 |
---|---|
_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 |
map | Map |
自定义别名:
在SqlMapConfig.xml中配置如下:
1 |
|
在mapper.xml配置文件中,就可以使用设置的别名了
提示:别名大小写不敏感
mappers(映射器)
Mapper配置的几种方法:
<mapper resource=” “ />
使用相对于类路径的资源(现在的使用方式)
如:<mapper resource="sqlmap/User.xml" />
<mapper class=” “ />
使用mapper接口类路径
如:<mapper class="fun.obey.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
<package name=””/>
注册指定包下的所有mapper接口
如:<package name="fun.obey.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
Mybatis进阶
parameterType(输入类型)
传递简单类型
参考入门的内容。
使用#{}占位符,或者${}进行sql拼接。
传递pojo对象
参考入门的内容。
Mybatis使用ognl表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称。
传递pojo包装对象
开发中通过可以使用pojo传递查询条件。
查询条件可能是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如查询用户信息的时候,将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
包装对象:Pojo类中的一个属性是另外一个pojo。
需求:根据用户名模糊查询用户信息,查询条件放到QueryVo的user属性中。
编写QueryVo
1 | public class QueryVo { |
Sql语句
SELECT * FROM user WHERE username LIKE '%张%'
Mapper.xml文件
1 | <select id="queryUserByQueryVo" parameterType="queryVo" resultType="user"> |
Mapper接口
List<User> queryUserByQueryVo(Query queryVo);
测试方法
在UserMapeprTest增加测试方法,如下:
1 |
|
效果
1 | DEBUG [main] - ==> Preparing: select * from user where username like '%%张%' |
resultType(输出类型)
输出简单类型
需求:查询用户表数据条数
sql:SELECT count(*) FROM user
Mapper.xml文件
1 | <select id = "queryUserCount" resultType = "int"> |
Mapper接口
int queryUserCount();
测试方法
在UserMapeprTest增加测试方法,如下:
1 |
|
效果
1 | DEBUG [main] - ==> Preparing: select count(*) from 'user' |
注意:输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。
resultMap
resultType可以指定将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系 ,resultMap实质上还需要将查询结果映射到pojo对象中。
resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
需求:查询订单表order的所有数据
sql:SELECT id, user_id, number, createtime, note FROM order
声明pojo对象
数据库如下:
1 | DROP TABLE IF EXISTS `orders`; |
Order对象:
1 | public class Order { |
Mapper.xml文件
创建OrderMapper.xml配置文件,如下:
1 |
|
Mapper接口
编写接口如下:
1 | public interface OrderMapper { |
测试方法
编写测试方法OrderMapperTest如下:
1 | public class OrderMapperTest { |
效果
1 | DEBUG [main] - ==> Preparing: select id,user_id,number,createtime,not from 'order' |
发现userId为null
解决方案:使用resultMap
使用resultMap
由于上边的mapper.xml中sql查询列(user_id)和Order类属性(userId)不一致,所以查询结果不能映射到pojo中。
需要定义resultMap,把orderResultMap将sql查询列(user_id)和Order类属性(userId)对应起来
改造OrderMapper.xml,如下:
1 |
|
效果
只需要修改Mapper.xml就可以了,再次测试结果如下:
1 | DEBUG [main] - ==> Preparing: select id,user_id,number,createtime,not from 'order' |
动态sql
通过mybatis提供的各种标签方法实现动态拼接sql。
需求:根据性别和名字查询用户
查询sql:
SELECT id, username, birthday, sex, address FROM user
WHERE sex = 1 AND username LIKE ‘%张%’
If标签
Mapper.xml文件
UserMapper.xml配置sql,如下:
1 | <!-- 根据条件查询用户 --> |
Mapper接口
List<User> queryUserByWhere(User user);
测试方法
在UserMapperTest添加测试方法,如下:
1 |
|
效果
1 | DEBUG [main] - ==> Preparing: SELECT id, username, birthday, sex, address FROM user WHERE sex = 1 AND username LIKE '张' |
如果注释掉user.setSex(“1”),测试结果如下图:
1 | DEBUG [main] - ==> Preparing: SELECT id, username, birthday, sex, address FROM user WHERE sex = 1 AND username LIKE null |
测试结果二很显然不合理。
按照之前所学的,要解决这个问题,需要编写多个sql,查询条件越多,需要编写的sql就更多了,显然这样是不靠谱的。
解决方案,使用动态sql的if标签
使用if标签
改造UserMapper.xml,如下:
1 | <!-- 根据条件查询用户 --> |
注意字符串类型的数据需要要做不等于空字符串校验。
效果
1 | DEBUG [main] - ==> Preparing: SELECT id, username, birthday, sex, address FROM user WHERE sex = 1 AND username LIKE '张' |
如上图所示,测试OK
Where标签
上面的sql还有where 1=1 这样的语句,很麻烦
可以使用where标签进行改造
改造UserMapper.xml,如下
1 | <!-- 根据条件查询用户 --> |
效果
1 | DEBUG [main] - ==> Preparing: SELECT id, username, birthday, sex, address FROM user WHERE sex = 1 AND username LIKE '张' |
Sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
把上面例子中的id, username, birthday, sex, address提取出来,作为sql片段,如下:
1 | <!-- 根据条件查询用户 --> |
注意:如果要使用别的Mapper.xml配置的sql片段,可以在refid前面加上对应的Mapper.xml的namespace
foreach标签
向sql传递数组或List,mybatis使用foreach解析,如下:
根据多个id查询用户信息
查询sql:
SELECT * FROM user WHERE id IN (1,10,24)
改造QueryVo
在pojo中定义list属性ids存储多个用户id,并添加getter/setter方法
Mapper.xml文件
UserMapper.xml添加sql,如下:
1 | <!-- 根据ids查询用户 --> |
测试方法如下图:
1 |
|
效果
1 | DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id in(?,?,?) |
关联查询
商品订单数据模型
一对一查询
需求:查询所有订单信息,关联查询下单用户信息。
注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。
sql语句:
1 | SELECT |
方法一:使用resultType
使用resultType,改造订单pojo类,此pojo类中包括了订单信息和用户信息
这样返回对象的时候,mybatis自动把用户信息也注入进来了
改造pojo类
OrderUser类继承Order类后OrderUser类包括了Order类的所有字段,只需要定义用户的信息字段即可,如下:
1 | public class OrderUser extends Order{ |
Mapper.xml
在UserMapper.xml添加sql,如下
1 | <!-- 查询订单,同时包含用户数据 --> |
Mapper接口
在UserMapper接口添加方法,如下:
1 | List<OrderUser> queryOrderUser(); |
测试方法:
在UserMapperTest添加测试方法,如下:
1 |
|
效果
测试结果如下图:
小结
定义专门的pojo类作为输出类型,其中定义了sql查询结果集所有的字段。此方法较为简单,企业中使用普遍。
方法二:使用resultMap
使用resultMap,定义专门的resultMap用于映射一对一查询结果。
改造pojo类
在Order类中加入User属性,user属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。
改造Order如下:
1 | public class Order{ |
Mapper.xml
这里resultMap指定orderUserResultMap,如下:
1 | <resultMap type="order" id="orderUserResultMap"> |
Mapper接口
1 | List<Order> queryOrderUserResultMap(); |
测试方法
在UserMapperTest增加测试方法,如下:
1 |
|
效果
测试效果如下图:
一对多查询
案例:查询所有用户信息及用户关联的订单信息。
用户信息和订单信息为一对多关系。
sql语句:
1 | SELECT |
修改pojo类
在User类中加入List1
2
3
4
5
6private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private List<Orders> orders; //附加属性
Mapper.xml
在UserMapper.xml添加sql,如下:
1 | <resultMap type="user" id="userOrderResultMap"> |
Mapper接口
编写UserMapper接口,如下:
1 | //一对多关联查询,用户内部包含该用户的订单 |
测试方法
在UserMapperTest增加测试方法,如下:
1 |
|
效果
1 | DEBUG [main] - ==> Preparing: SELECT u.id, u.username, u.birthday, u.sex, u.address, o.id oid, o.user_id, o.number, o.createtime, o.note FROM user u LEFT JOIN orders o ON u.id = o.user_id |
Mybatis整合spring
整合思路
- SqlSessionFactory对象应该放到spring容器中作为单例存在。
- 传统dao的开发方式中,应该从spring容器中获得sqlsession对象。
- Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象。
- 数据库的连接以及数据库连接池事务管理都交给spring容器来完成。
整合需要的jar包
- spring的jar包
- Mybatis的jar包
- Spring+mybatis的整合包。
- Mysql的数据库驱动jar包。
- 数据库连接池的jar包。
整合的步骤
- 创建工程
- 导入jar包
加入配置文件
- mybatisSpring的配置文件
- 的配置文件sqlmapConfig.xml
a)数据库连接及连接池
b)事务管理(暂时可以不配置)
c)sqlsessionFactory对象,配置到spring容器中
d)mapeer代理对象或者是dao实现类配置到spring容器中。
SqlMapConfig.xml
配置文件是SqlMapConfig.xml,如下:
1 |
|
applicationContext.xml
SqlSessionFactoryBean属于mybatis-spring这个jar包
对于spring来说,mybatis是另外一个架构,需要整合jar包。
applicationContext.xml,配置内容如下:
1 |
|
db.properties
1 | jdbc.driver=com.mysql.jdbc.Driver |
log4j.properties
1 | # Global logging configuration |
Dao的开发
两种dao的实现方式:
- 原始dao的开发方式
- 使用Mapper代理形式开发方式
a)直接配置Mapper代理
b)使用扫描包配置Mapper代理
需求:
- 实现根据用户id查询
- 实现根据用户名模糊查询
- 添加用户
创建pojo
1 | public class User { |
传统dao的开发方式
原始的DAO开发接口+实现类来完成。
需要dao实现类需要继承SqlsessionDaoSupport类
实现Mapper.xml
编写User.xml配置文件,如下:
1 |
|
加载Mapper.xml
在SqlMapConfig进行配置:
1 | <mappers> |
实现UserDao接口
1 | public interface UserDao { |
实现UserDaoImpl实现类
编写DAO实现类,实现类必须集成SqlSessionDaoSupport
SqlSessionDaoSupport提供getSqlSession()方法来获取SqlSession
1 | public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { |
配置dao
把dao实现类配置到spring容器中,如下:
1 | <!-- 配置SqlSessionFactory --> |
测试方法
创建测试方法,可以直接创建测试Junit用例。
编写测试方法如下:
1 | public class UserDaoTest { |
Mapper代理形式开发dao
实现Mapper.xml
编写UserMapper.xml配置文件,如下:
1 |
|
实现UserMapper接口
1 | public interface UserMapper { |
方式一:配置mapper代理
在applicationContext.xml添加配置
MapperFactoryBean也是属于mybatis-spring整合包
1 | <!-- Mapper代理的方式开发方式一,配置Mapper代理对象 --> |
测试方法
1 | public class UserMapperTest { |
方式二:扫描包形式配置mapper
1 | <!-- Mapper代理的方式开发方式二,扫描包方式配置代理 --> |
提示:每个mapper代理对象的id就是类名,首字母小写