1.MyBatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
Mybatis网站: https://github.com/mybatis/mybatis-3
2.MyBatis特性
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Ordinary Java Objects,普通的Java对象)映射成数据库中的记录
MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
3.MyBatis和其他持久层技术的对比
JDBC
SQL夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
代码冗长,开发效率低
Hibernate 和JPA
操作简便,开发效率高
程序中的长难复杂SQL需要绕过框架
内部自动生产的SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL和Java编码分开,功能边界清晰。Java代码专注业务、SQL 语句专注数据
开发效率稍逊于Hlbernate,但是完全能够接受
4.MyBatis环境搭建
创建Maven工程
选择SDK
**修改Maven设置 **
配置pom.xml配置文件
设置打包方式为jar,并引入以下依赖
< dependencies>
< dependency>
< groupId> org.mybatis</ groupId>
< artifactId> mybatis</ artifactId>
< version> 3.5.7</ version>
</ dependency>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
< version> 4.12</ version>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 8.0.19</ version>
</ dependency>
</ dependencies>
创建MyBatis的核心配置文件
一:在resources文件夹下创建
二:配置mybatis核心配置文件
<?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" >
< configuration>
< environments default = " development" >
< environment id = " development" >
< transactionManager type = " JDBC" />
< dataSource type = " POOLED" >
< property name = " driver" value = " com.mysql.cj.jdbc.Driver" />
< property name = " url" value = " jdbc:mysql://localhost:3306/mybatis" />
< property name = " username" value = " root" />
< property name = " password" value = " root" />
</ dataSource>
</ environment>
</ environments>
< mappers>
< mapper resource = " org/mybatis/example/BlogMapper.xml" />
</ mappers>
</ configuration>
创建mapper接口
为什么要创建mapper接口?因为MyBatis中有面向接口编程的功能,当调用接口中的方法时就会自动匹配sql语句,并且执行。
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
创建MyBatis的映射文件,执行sql测试添加功能
相关概念:ORM (O bject R elationship M apping)对象关系映射。
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
mapper接口
public interface UserMapper {
public int addUser ( ) ;
}
映射文件
<?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 = " cn.pdsu.mybatis.mapper.UserMapper" >
< insert id = " addUser" >
insert into user value (3,"珊瑚宫心海","123456",18,'女')
</ insert>
</ mapper>
然后在MyBatis核心配置文件中引入映射文件,resource路径为mapper映射文件的路径
创建测试类
@Test
public void test1 ( ) throws IOException {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ( ) ;
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder. build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
UserMapper mapper = sqlSession. getMapper ( UserMapper . class ) ;
int result = mapper. addUser ( ) ;
System . out. println ( "result:" + result) ;
}
测试成功
加入log4j日志的配置文件
相应依赖
< dependency>
< groupId> log4j</ groupId>
< artifactId> log4j</ artifactId>
< version> 1.2.12</ version>
</ dependency>
配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
< log4j: configuration xmlns: log4j= " http://jakarta.apache.org/log4j/" >
< appender name = " STDOUT" class = " org.apache.log4j.ConsoleAppender" >
< param name = " Encoding" value = " UTF-8" />
< layout class = " org.apache.log4j.PatternLayout" >
< param name = " ConversionPattern" value = " %-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</ layout>
</ appender>
< logger name = " java.sql" >
< level value = " debug" />
</ logger>
< logger name = " org.apache.ibatis" >
< level value = " info" />
</ logger> < root> < level value = " debug" />
< appender-ref ref = " STDOUT" />
</ root>
</ log4j: configuration>
执行程序,打印日志信息
5.MyBatis框架的对数据库的CRUD功能 5.1添加功能
在mapper接口中添加方法
创建实体类的映射文件,并添加插入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 = " cn.pdsu.mybatis.mapper.UserMapper" >
< insert id = " addUser" >
insert into user value (4,"申鹤","123456",18,'女')
</ insert>
</ mapper>
在Mybatis核心配置文件中引入实体类的映射文件
< mappers>
< mapper resource = " mappers/UserMapper.xml" />
</ mappers>
编写测试类
@Test
public void test1 ( ) throws IOException {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ( ) ;
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder. build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
UserMapper mapper = sqlSession. getMapper ( UserMapper . class ) ;
int result = mapper. addUser ( ) ;
System . out. println ( "result:" + result) ;
}
5.2修改功能
在mapper接口中添加相应的方法
在mapper映射文件中添加sql语句
< update id = " updateUser" >
update user set age = 19 where username = "枫原万叶"
</ update>
在Mybatis核心配置文件中引入实体类的映射文件(上一步已完成)
编写测试类
@Test
public void test12 ( ) throws IOException {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ( ) ;
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder. build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
UserMapper mapper = sqlSession. getMapper ( UserMapper . class ) ;
int result = mapper. updateUser ( ) ;
System . out. println ( "result:" + result) ;
}
修改前:
修改后:
5.3删除功能
在mapper接口中添加相应的方法
在mapper映射文件中添加sql语句
< delete id = " deleteUser" >
delete user from user where name = "珊瑚宫心海"
</ delete>
在Mybatis核心配置文件中引入实体类的映射文件(上一步已完成)
编写测试类
@Test
public void test12 ( ) throws IOException {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ( ) ;
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder. build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
UserMapper mapper = sqlSession. getMapper ( UserMapper . class ) ;
int result = mapper. updateUser ( ) ;
System . out. println ( "result:" + result) ;
}
修改前:
修改后:
5.4查询功能 查询功能注意的点:
查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系
resultType:自动映射,用于属性名和表中字段名一致的情况
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
映射文件:
< select id = " findUser" resultType = " cn.pdsu.mybatis.pojo.User" >
select * from user where id = 1
</ select>
当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异TooManyResultsException
;但是若查询的数据只有一条,可以使用实体类或集合作为返回值
映射文件:
< select id = " findAllUser" resultType = " cn.pdsu.mybatis.pojo.User" >
select * from user
</ select>
@Test
public void test5 ( ) throws IOException {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder ( ) ;
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder. build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
UserMapper mapper = sqlSession. getMapper ( UserMapper . class ) ;
List < User > allUser = mapper. findAllUser ( ) ;
allUser. forEach ( System . out:: println ) ;
}
6.MyBatis核心配置文件properties 对于之前的引入数据源的方式,也可以通过propertise配置文件来配置数据源
前面加jdbc前缀的目的是为了区分多个字段名相同的情况
MyBatis核心配置文件
< configuration>
< properties resource = " jdbc.properties" > </ properties>
< environments default = " development" >
< environment id = " development" >
< transactionManager type = " JDBC" />
< dataSource type = " POOLED" >
< property name = " driver" value = " ${jdbc.driver}" />
< property name = " url" value = " ${jdbc.url}" />
< property name = " username" value = " ${jdbc.username}" />
< property name = " password" value = " ${jdbc.password}" />
</ dataSource>
</ environment>
</ environments>
< mappers>
< mapper resource = " mappers/UserMapper.xml" />
</ mappers>
</ configuration>
7.MyBatis核心配置文件typeAllases 对于之前的查询映射文件,标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系 ,对于全类名可以采用核心配置文件中的typeAliases标签来设置类型别名
< configuration>
< properties resource = " jdbc.properties" > </ properties>
< typeAliases>
< typeAlias type = " cn.pdsu.mybatis.pojo.User" > </ typeAlias>
</ typeAliases>
.........
</ configuration>
在核心配置文件当中设置了类型别名后就可以在mapper映射文件当中将resultType的全类名替换为该类型别名
< select id = " findUser" resultType = " User" >
select * from user where id = 1
</ select>
< select id = " findAllUser" resultType = " User" >
select * from user
</ select>
但是通常情况下不会使用typeAlias 标签,而是会使用package标签
< typeAliases>
< package name = " cn.pdsu.mybatis.pojo" > </ package>
</ typeAliases>
8.MyBatis核心配置文件mappers 对于之前的在MyBatis核心配置文件中引入映射文件是采用以下方式的
< mappers>
< mapper resource = " mappers/UserMapper.xml" />
</ mappers>
此种方式是每一个实体类对应一个mapper接口,每一个mapper接口对应一个映射文件,当实体类较多时就需要引入多个映射文件,此时就可以采用和以上设置全类名的方式相同的方式以包为单位引入映射文件
创建包的方式,以斜线分割,如果用点的话创建的将会是一个文件夹
主要注意的是:
mapper接口所在的包和映射文件所在的包一致
mapper接口要和映射文件的名字一致
< mappers>
< package name = " cn.pdsu.mybatis.mapper" />
</ mappers>
测试成功
9.MyBatis核心配置文件、映射文件模板配置 9.1MyBatis核心配置文件
生成配置文件
9.2映射文件
10.封装sqlSessionUtils工具类
sqlSessionUtils工具类
public class sqlSessionUtils {
public static SqlSession getSqlSession ( ) {
SqlSession sqlSession = null ;
try {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( is) ;
sqlSession = sqlSessionFactory. openSession ( true ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
return sqlSession;
}
}
测试类
@Test
public void test6 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
List < User > userList = mapper. test1 ( ) ;
userList. forEach ( System . out:: println ) ;
}
11.MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式:${}和 #{}
11.1mybatis获取参数值得各种情况 11.1.1单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号
若不加单引号为下面情况
测试:
映射文件:值得注意的是${}或则#{}中的参数名可以为任何值,重要的不是参数名而是参数值
< select id = " getUserByName" resultType = " User" >
-- select * from user where username = '${username}'
select * from user where username = #{username}
</ select>
测试类:
@Test
public void test6 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
User user = mapper. getUserByName ( "枫原万叶" ) ;
System . out. println ( user) ;
}
11.1.2多个字面量类型的参数 若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以两种方式进行存储。
以arg0,arg1…为键,以参数为值;
以param1,param2…为键,以参数为值;
因此只需要通过${}或#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
< select id = " checkLogin" resultType = " User" >
select * from user where username = #{arg0} and password = #{arg1}
</ select>
测试类:
@Test
public void test7 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
User user = mapper. checkLogin ( "枫原万叶" , "123456" ) ;
System . out. println ( user) ;
}
11.1.3Map集合类型的参数 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
mapper接口中的方法参数为map集合
public User checkLoginByMap ( Map < String , Object > map) ;
映射文件
< select id = " checkLoginByMap" resultType = " User" >
select * from user where username = #{username} and password = #{password}
</ select>
测试方法:
@Test
public void test8 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "username" , "枫原万叶" ) ;
map. put ( "password" , "123456" ) ;
User user = mapper. checkLoginByMap ( map) ;
System . out. println ( user) ;
}
11.1.4实体类类型的参数 若mapper接口中的方法参数为实体类对象
时此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值 ,注意${}需要手动加单引号。
映射文件
< insert id = " addUser" >
insert into user values(#{id},#{username},#{password},#{age},#{sex})
</ insert>
测试类
@Test
public void test9 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
mapper. addUser ( new User ( 3 , "珊瑚宫心海" , "123456" , 18 , '女' ) ) ;
}
11.1.5使用@Param标识参数 可以通过@Param
注解标识mapper接口中的方法参数
此时,MyBatis会将这些参数放在map集合中,
以@Param注解的value属性值为键,以参数为值;
以param1,param2…为键,以参数为值;
只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
mapper接口中的方法
public User checkLoginByParam (
@Param ( "username" ) String username,
@Param ( "password" ) String password) ;
mapper接口的映射文件
< select id = " checkLoginByParam" resultType = " User" >
select * from user where username = #{username} and password = #{password}
</ select>
测试类
public void test10 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
ParameterMapper mapper = sqlSession. getMapper ( ParameterMapper . class ) ;
User user = mapper. checkLoginByParam ( "枫原万叶" , "123456" ) ;
System . out. println ( user) ;
}
12.MyBatis的各种查询功能 12.1查询一个实体类对象 mapper接口中的方法
public User getUserBuId ( @Param ( "id" ) int id) ;
映射文件
< select id = " getUserBuId" resultType = " User" >
select * from user where id = #{id}
</ select>
测试类
@Test
public void test11 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
User user = mapper. getUserBuId ( 1 ) ;
System . out. println ( user) ;
}
12.2查询一个List集合 mapper接口中的方法
public List \< User \> findAllUser ( ) ;
映射文件
< select id = " findAllUser" resultType = " User" >
select * from user
</ select>
测试类
@Test
public void test12 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
List < User > user = mapper. findAllUser ( ) ;
user. forEach ( System . out:: println ) ;
}
12.3查询单个数据 mapper接口中的方法
public int findUserCount ( ) ;
映射文件
< ! -- public int findUserCount ( ) ;
resultType定义返回值类型为int -- >
< select id= "findUserCount" resultType= "int" >
select count ( * ) from user
< / select>
测试类
@Test
public void test13 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
int userCount = mapper. findUserCount ( ) ;
System . out. println ( userCount) ;
}
12.4查询一条数据为map集合 mapper接口中的方法
public Map < String , Object > getUserByIdToMap ( @Param ( "id" ) int id ) ;
映射文件
< ! -- public Map < String , Object > getUserByIdToMap ( @Param ( "id" ) int id ) ; -- >
< select id= "getUserByIdToMap" resultType= "map" >
select * from user where id = #{ id}
< / select>
测试类
@Test
public void test14 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
Map < String , Object > map = mapper. getUserByIdToMap ( 1 ) ;
System . out. println ( map) ;
}
12.5查询多条数据为map集合 第一种方法:
mapper接口中的方法
public List < Map < String , Object > > getAllUserByIdToMap ( ) ;
映射文件
< ! -- public Map < String , Object > getAllUserByIdToMap ( ) ; -- >
< select id= "getAllUserByIdToMap" resultType= "map" >
select * from user
< / select>
测试类
@Test
public void test15 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
List < Map < String , Object > > list = mapper. getAllUserByIdToMap ( ) ;
list. forEach ( System . out:: println ) ;
}
第二种方法:
将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合
mapper接口中的方法
@MapKey ( "id" )
public Map < String , Object > getAllUserByIdToMap ( ) ;
映射文件
< ! -- public Map < String , Object > getAllUserByIdToMap ( ) ; -- >
< select id= "getAllUserByIdToMap" resultType= "map" >
select * from user
< / select>
测试类
@Test
public void test15 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
SelectMapper mapper = sqlSession. getMapper ( SelectMapper . class ) ;
Map < String , Object > map = mapper. getAllUserByIdToMap ( ) ;
System . out. println ( map) ;
}
13.特殊SQL执行(只能使用${}的情况) 13.1模糊查询 对于模糊查询,如下面语句所示
select * from user where username like "%#{username}%"
当使用模糊查询时,如果采用#{}的方式,那么此sql语句中的参数就会当成一整个字符串,就会出现异常的情况
对于以上问题,就可以采用${}的方式进行解决
select * from user where username like "%${username}%"
还有一种最常用的方式
select \* from user where username like "%"
13.2批量删除 delete from user where id in ( \#{ids})
对于以上删除的SQL语句,采用#{}的方式来设置参数,由于#{}的方式会自动添加引号,这样就会导致SQL语句变成下面
//错误的SQL语句 delete from user where id in (“1,2”)
//正确的SQL语句 delete from user where id in (1,2)
对于以上问,就必须要使用${}的方式来设置参数
delete from user where id in ( \${ids})
13.3动态获取表名 mapper接口中的方法
public List < User > findUserByTable ( @Param ( "tableName" ) String tableName) ;
映射文件
< ! -- public List < User > findUserByTable ( @Param ( "tableName" ) String tableName) ; -- >
< select id= "findUserByTable" resultType= "User" >
select * from ${ tableName}
< / select>
13.4添加功能获取自增的主键 映射文件
< ! -- public void addUser ( User user) ;
useGeneratedKeys设置当前主键使用了自动递增的功能
keyProperty将自增的主键的值赋值给传输到映射文件中参数的某个属性
此sql语句中就是将id赋值给null
-- >
< insert id= "addUser" useGeneratedKeys= "true" keyProperty= "id" >
insert into user values ( null , #{ username} , #{ password} , #{ age} , #{ sex} )
< / insert>
测试类
@Test
public void test4 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
nuClearMapper mapper = sqlSession. getMapper ( nuClearMapper. class ) ;
mapper. addUser ( new User ( null , "可莉" , "123456" , 18 , '女' ) ) ;
}
14.处理属性名和字段名不一样的问题 对于实体类和数据库表中的属性名不一致的情况,实体类中属性的命名方式是驼峰原则,而数据库表中的属性时使用的字段命名规则。
如果实体类中和数据库表中的字段属性名不同的话,就会出现映射不到的问题,那么返回的值就会为空
如下面的查询功能:
对于属性名和字段名不一样的情况,其映射关系该如何处理呢
14.1为sql语句中字段设置别名 < ! -- public List < Employee > findAll ( ) ; -- >
< select id= "findAll" resultType= "Employee" >
select * from t_employee
select eid, e_name eName, did from t_employee
< / select>
14.2在Mybatis核心配置文件中设置全局配置,将“_”自动映射为驼峰 < ! -- 设置Mybatis 的全局配置-- >
< settings>
< ! -- 将"_" 映射为驼峰,e_name-> eName-- >
< setting name= "mapUnderscoreToCamelCase" value= "true" / >
< / settings>
14.3通过resultMap设置自定义映射
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中数据库表中的字段名
如下:
< resultMap id = " EmployeeMap" type = " Employee" >
< id property = " eid" column = " eid" > </ id>
< result property = " eName" column = " e_name" > </ result>
< result property = " did" column = " did" > </ result>
</ resultMap>
< select id = " findAll" resultMap = " EmployeeMap" >
select * from t_employee
</ select>
15.多对一映射处理(多个员工对应一个部门) 在员工表中添加所对应部门的属性
(一):级联方式处理多对一映射关系
查询员工信息以及员工所对应的部门信息
< ! -- 设置属性和字段的映射关系-- >
< resultMap id= "empAndDeptOne" type= "Employee" >
< id property= "eid" column= "eid" > < / id>
< result property= "eName" column= "e_name" > < / result>
< result property= "did" column= "did" > < / result>
< result property= "dept.did" column= "did" > < / result>
< result property= "dept.dName" column= "d_name" > < / result>
< / resultMap>
< ! -- Employee getEmpAndDept ( @Param ( "eid" ) int eid) ; -- >
< select id= "getEmpAndDept" resultMap= "empAndDeptOne" >
select * from t_employee left join t_dept
on t_employee. did = t_dept. did
where t_employee. did = #{ eid}
< / select>
(二):使用association处理多对一映射关系
< resultMap id= "empAndDeptTwo" type= "Employee" >
< id property= "eid" column= "eid" > < / id>
< result property= "eName" column= "e_name" > < / result>
< result property= "did" column= "did" > < / result>
< association property= "dept" javaType= "Dept" >
< id property= "did" column= "did" > < / id>
< result property= "dName" column= "d_name" > < / result>
< / association>
< / resultMap>
< ! -- Employee getEmpAndDept ( @Param ( "eid" ) int eid) ; -- >
< select id= "getEmpAndDept" resultMap= "empAndDeptTwo" >
select * from t_employee left join t_dept
on t_employee. did = t_dept. did
where t_employee. eid = #{ eid}
< / select>
(三):使用分步查询处理多对一映射关系
EmployeeMapper映射文件:
< ! -- public Employee getEmployeeByStepOne ( @Param ( "eid" ) int eid) ; -- >
< resultMap id= "getEmployeeByStepOneMap" type= "Employee" >
< id property= "eid" column= "eid" > < / id>
< result property= "eName" column= "e_name" > < / result>
< result property= "did" column= "did" > < / result>
< result property= "dept.did" column= "did" > < / result>
< result property= "dept.dName" column= "d_name" > < / result>
< ! -- select:设置分步查询SQL 的唯一标识(当前mapper接口的全类名. 方法名)
column:设置分步查询的条件(即根据did进行查询部门信息)
-- >
< association property= "dept" select= "cn.pdsu.mybatis.mapper.DeptMapper.getDeptByStepOne" column= "did" >
< / association>
< / resultMap>
< select id= "getEmployeeByStepOne" resultMap= "getEmployeeByStepOneMap" >
select * from t_employee where eid = #{ eid}
< / select>
DeptMapper映射文件
< ! -- public Dept getDeptByStepOne ( @Param ( "did" ) int did) ; -- >
< ! -- 根据核心配置文件设置属性和字段之间的关系-- >
< select id= "getDeptByStepOne" resultType= "Dept" >
select * from t_dept where did = #{ did}
< / select>
分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
此时可通过association中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=”lazy(延迟加载)|eager(立即加载)”
测试:
对员工姓名进行查询:
@Test
public void test3 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
EmployeeMapper mapper = sqlSession. getMapper ( EmployeeMapper . class ) ;
Employee empAndDept = mapper. getEmployeeByStepOne ( 1 ) ;
System . out. println ( empAndDept. geteName ( ) ) ;
}
核心配置文件配置:
< ! -- 设置Mybatis 的全局配置-- >
< settings>
< ! -- 将"_" 映射为驼峰,e_name-> eName-- >
< setting name= "mapUnderscoreToCamelCase" value= "true" / >
< ! -- 对所有的分布查询开启延迟加载-- >
< setting name= "lazyLoadingEnabled" value= "true" / >
< / settings>
< ! -- public Employee getEmployeeByStepOne ( @Param ( "eid" ) int eid) ; -- >
< resultMap id= "getEmployeeByStepOneMap" type= "Employee" >
< id property= "eid" column= "eid" > < / id>
< result property= "eName" column= "e_name" > < / result>
< result property= "did" column= "did" > < / result>
< result property= "dept.did" column= "did" > < / result>
< result property= "dept.dName" column= "d_name" > < / result>
< ! -- select:设置分步查询SQL 的唯一标识(当前mapper接口的全类名. 方法名)
column:设置分步查询的条件(即根据did进行查询部门信息)
fetchType:开启全局延迟加载之后,可以通过此属性手动可控制延迟加载的效果
-- >
< association property= "dept"
select= "cn.pdsu.mybatis.mapper.DeptMapper.getDeptByStepOne"
column= "did"
fetchType= "lazy" > < / association>
< / resultMap>
< select id= "getEmployeeByStepOne" resultMap= "getEmployeeByStepOneMap" >
select * from t_employee where eid = #{ eid}
< / select>
当fetchType属性的值为lazy时,开启延时加载
当fetchType属性的值为eager时,立即加载
16.一对多映射处理(一个部门含有多个员工)
一:使用collection处理一对多映射关系
collection标签中
property:处理一对多关系的属性(对应的时Dept类中的emps属性)
ofType:该属性所对应的集合中储存数据的类型(对应属性emps的数据类型为Employee)
< ! -- public Dept getDeptAndEmps ( @Param ( "did" ) int did) ; -- >
< resultMap id= "DeptAndEmpsMap" type= "Dept" >
< id property= "did" column= "did" > < / id>
< result property= "dName" column= "d_name" > < / result>
< collection property= "emps" ofType= "Employee" >
< id property= "eid" column= "eid" > < / id>
< result property= "eName" column= "e_name" > < / result>
< / collection>
< / resultMap>
< select id= "getDeptAndEmps" resultMap= "DeptAndEmpsMap" >
select * from t_dept left join
t_employee on t_dept. did = t_employee. did
where t_dept. did = #{ did}
< / select>
二:使用分步查询处理一对多映射关系
DeptMapper
< ! -- public Dept getDeptAndEmpsStepOne ( @Param ( "did" ) int did) ; -- >
< resultMap id= "DeptAndEmpsStepOneMap" type= "Dept" >
< id property= "did" column= "did" > < / id>
< result property= "dName" column= "d_name" > < / result>
< collection property= "emps"
< ! -- select:设置分步查询SQL 的唯一标识(当前mapper接口的全类名. 方法名)
column:设置分步查询的条件(即根据did进行查询员工信息)
-- >
select= "cn.pdsu.mybatis.mapper.EmployeeMapper.getDeptAndEmpsStepTwo"
column= "did" >
< / collection>
< / resultMap>
< select id= "getDeptAndEmpsStepOne" resultMap= "DeptAndEmpsStepOneMap" >
select * from t_dept where did = #{ did}
< / select>
其中collection 标签中的select的属性值为对应实体类接口中的方法
EmployeeMapper
< select id = " getDeptAndEmpsStepTwo" resultType = " Employee" >
select * from t_employee where did = #{did}
</ select>
对于一对多的情况,同样可以实现延迟加载
17.动态SQL Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能 ,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
17.1if标签 if标签可通过test
属性的表达式进行判断,若表达式的结果为true,则标签中的内容会添加到SQL语句中
为sql语句添加where 1 = 1的条件设置为sql语句恒成立
< ! -- public List < Employee > getEmpByCondition ( Employee employee) -- >
< select id= "getEmpByCondition" resultType= "Employee" >
select * from t_employee where 1 = 1
< if test= "eid != null and eid != ''" >
and eid = #{ eid}
< / if >
< if test= "eName != null and eName != ''" >
and e_name = #{ eName}
< / if >
< if test= "did != null and did != ''" >
and did = #{ did}
< / if >
< / select>
17.2where标签 where和if一般结合使用: where标签可以动态生成where关键字,并且将内容前多余的and或or去掉。
< ! -- public List < Employee > getEmpByCondition ( Employee employee) -- >
< select id= "getEmpByCondition" resultType= "Employee" >
select * from t_employee
< where>
< if test= "eid != null and eid != ''" >
and eid = #{ eid}
< / if >
< if test= "eName != null and eName != ''" >
and e_name = #{ eName}
< / if >
< if test= "did != null and did != ''" >
and did = #{ did}
< / if >
< / where>
< / select>
17.3trim标签 trim用于去掉或添加标签中的内容
常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
下面的例子就是在sql语句中前面添加where关键字,并且去掉后面的and关键字
< select id = " getEmpByCondition" resultType = " Employee" >
select * from t_employee
< trim prefix = " where" suffixOverrides = " and|or" >
< if test = " eid != null and eid != ''" >
eid = #{eid} and
</ if>
< if test = " eName != null and eName != ''" >
e_name = #{eName} and
</ if>
< if test = " did != null and did != ''" >
did = #{did} and
</ if>
</ trim>
</ select>
17.4choose、when、otherwise choose、when、otherwise相当于if…else if..else,其中choose是父标签
when标签至少有一个,otherwise标签最多有一个
按照when标签的顺序,当有一个when标签满足条件时,就结束循环,
当when标签都不满足时,就会执行otherwise标签中的内容,有点类似于switch语句
< ! -- public List < Employee > getEmpByChoose ( Employee employee) ; -- >
< select id= "getEmpByChoose" resultType= "Employee" >
select * from t_employee
< where>
< choose>
< when test= "eid != null and eid != ''" >
eid = #{ eid}
< / when>
< when test= "eName != null and eName != ''" >
e_name = #{ eName}
< / when>
< when test= "did != null and did != ''" >
did = #{ did}
< / when>
< otherwise>
eid = 3
< / otherwise>
< / choose>
< / where>
< / select>
17.5foreach循环标签 foreach 标签经常用于遍历集合,构建in条件语句或者批量操作语句。
相关属性
collection:设置要循环的数组或集合
item:表示集合或数组中的每一个数据
separator:设置循环体之间的分隔符
open:设置foreach标签中的内容的开始符
close:设置foreach标签中的内容的结束符
例子:
一:根据eid批量删除,相关的SQL语句是
delete from t_employee where eid in ( 1 , 2 )
mapper接口
public int deleteByArray ( @Param ( "eids" ) Integer [ ] eid) ;
映射文件
表示对eids数据进行循环,eid作为数组中的每一个数据,每一个数据之间的分隔符为“,”,并且以“(”开始,以“)”结束。
< delete id = " deleteByArray" >
delete from t_employee where eid in
< foreach collection = " eids" item = " eid" separator = " ," open = " (" close = " )" >
#{eid}
</ foreach>
</ delete>
二:实现批量添加,相关的SQL语句是
insert into t_employee values ( ?, ?, ?) ,
( ?, ?, ?) ,
( ?, ?, ?) , ( ?, ?, ?)
mapper接口
public int insertByArray ( @Param ( "emps" ) List < Employee > employees) ;
映射文件
foreach标签中意思是对emps集合进行循环,eid为集合中的每一个数据,以“,”为分隔符
< ! -- public int insertByArray ( List < Employee > employees) ; -- >
< insert id= "insertByArray" >
insert into t_employee values
< foreach collection= "emps" item= "emp" separator= "," >
( #{ emp. eid} , #{ emp. eName} , #{ emp. did} )
< / foreach>
< / insert>
测试类
public void test4 ( ) {
SqlSession sqlSession = SqlSessionUtils . getSqlSession ( ) ;
DynamicSQLMapper mapper = sqlSession. getMapper ( DynamicSQLMapper . class ) ;
Employee employee1 = new Employee ( 5 , "班尼特" , 1 ) ;
Employee employee2 = new Employee ( 6 , "行秋" , 1 ) ;
Employee employee3 = new Employee ( 7 , "申鹤" , 2 ) ;
Employee employee4 = new Employee ( 8 , "钟离" , 2 ) ;
List < Employee > employees = Arrays . asList ( employee1, employee2, employee3, employee4) ;
int i = mapper. insertByArray ( employees) ;
System . out. println ( i) ;
}
17.6sql标签(SQL片段) sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
例子:当查询职工信息时,使用sql片段实现
< sql id = " selectSql" > eid,e_name,did</ sql>
< select id = " getEmpByCondition" resultType = " Employee" >
select < include refid = " selectSql" > </ include> from t_employee
< trim prefix = " where" suffixOverrides = " and|or" >
< if test = " eid != null and eid != ''" >
eid = #{eid} and
</ if>
< if test = " eName != null and eName != ''" >
e_name = #{eName} and
</ if>
< if test = " did != null and did != ''" >
did = #{did} and
</ if>
</ trim>
</ select>
18.MyBatis的缓存 18.1MyBatis的缓存机制 Mybatis 缓存机制原理是将第一次从数据库 SQL 查询的结果数据保存到缓存(内存中),当下一次 SQL 查询和第一次相同,如果缓存中有数据则直接获取,而不用再从数据库获取,从而减少数据库访问频率,大大提升数据库性能。
18.2缓存机制分类 MyBatis
提供了两种缓存机制:一级缓存和二级缓存
一级缓存是 SqlSession
级别的缓存,它是默认开启的。在操作数据库时需要构造 SqlSession 对象,它是应用程序与持久存储层之间执行交互操作的一个单线程对象 ,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的 SqlSession 之间的缓存数据区(HashMap)是互相不影响的。
二级缓存是Mapper
级别的缓存,多个 SqlSession 去操作同一个 Mapper 的 sql 语句,多个 SqlSession 可以共用二级缓存 ,二级缓存是跨 SqlSession 的。但是,二级缓存默认是关闭的,需要手动开启。
18.3Mybatis一级缓存 18.3.1一级缓存说明 在操作数据库时需要构造 SqlSession
对象,在对象中存在一个HashMap
用于存储缓存数据。
通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
一级缓存满足条件如下:
同一个 SqlSession 对象
同一个SqlSession两次查询期间没有进行增删改操作
相同的 SQL 语句和参数
注:使用 SqlSession.clearCache( ) 方法可以强制清除一级缓存
18.3.2一级缓存测试 18.3.2.1使用同一个SqlSession 既然每个 SqlSession 都会有自己的一个缓存,那么我们用同一个 SqlSession 是不是就能感受到一级缓存的存在呢?调用多次 getMapper 方法,生成对应的SQL语句,判断每次SQL语句是从缓存中取还是对数据库进行操作,下面的例子来证明一下
@Test
public void cacheTest ( ) {
List < UserEntity > userEntities = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities) ;
List < UserEntity > userEntities2 = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities2) ;
List < UserEntity > userEntities3 = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities3) ;
}
执行测试,输出结果如下:
可以看到只执行了一次查询操作
2020 - 08 - 10 16 : 14 : 44 , 790 [ main] [ mapper. UserMapper. selectUserByAge] - [ DEBUG ] == > Preparing : select * from tb_user where age > ?
2020 - 08 - 10 16 : 14 : 44 , 837 [ main] [ mapper. UserMapper. selectUserByAge] - [ DEBUG ] == > Parameters : 20 ( Integer )
2020 - 08 - 10 16 : 14 : 44 , 884 [ main] [ mapper. UserMapper. selectUserByAge] - [ DEBUG ] <= = Total : 7
[ UserEntity { id= 1 , userName= '张三' , password= '123456' , name= '张三' , age= 22 , sex= 1 , birthday= Sun Sep 02 00 : 00 : 00 IRKST 1990 , created= '2020 - 06 - 17 09 : 30 : 58.0 ', updated= '2020 - 06 - 17 09 : 30 : 58.0 ', interests= null }
[ UserEntity { id= 1 , userName= '张三' , password= '123456' , name= '张三' , age= 22 , sex= 1 , birthday= Sun Sep 02 00 : 00 : 00 IRKST 1990 , created= '2020 - 06 - 17 09 : 30 : 58.0 ', updated= '2020 - 06 - 17 09 : 30 : 58.0 ', interests= null }
18.3.2.2两次查询期间进行增删改操作 接着我在第一条和第二条 SQL语句 之间插入更新的 SQL 语句,代码如下:
@Test
public void cacheTest ( ) {
List < UserEntity > userEntities = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities) ;
int result = userMapper. updateUser ( 1 , "张三" ) ;
sqlSession. commit ( ) ;
List < UserEntity > userEntities2 = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities2) ;
}
执行测试,结果如下:
020-08-10 16:20:47, 384 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Preparing: select * from tb_user where age > ?
2020-08-10 16:20:47, 431 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Parameters: 20( Integer)
2020-08-10 16:20:47, 478 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] <== Total: 7
[ UserEntity{ id=1, userName='张三' , password='123456' , name='张三' , age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0' , updated='2020-06-17 09:30:58.0' , interests=null}
2020-08-10 16:20:47, 478 [main] [mapper.UserMapper.updateUser] - [DEBUG] ==> Preparing: update tb_user set name=? where id=?
2020-08-10 16:20:47, 478 [main] [mapper.UserMapper.updateUser] - [DEBUG] ==> Parameters: 张三( String) , 1( Integer)
2020-08-10 16:20:47, 478 [main] [mapper.UserMapper.updateUser] - [DEBUG] <== Updates: 1
2020-08-10 16:20:47, 493 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Preparing: select * from tb_user where age > ?
2020-08-10 16:20:47, 493 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Parameters: 20( Integer)
2020-08-10 16:20:47, 509 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] <== Total: 7
[ UserEntity{ id=1, userName='张三' , password='123456' , name='张三' , age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0' , updated='2020-06-17 09:30:58.0' , interests=null} ,
[ UserEntity{ id=1, userName='张三' , password='123456' , name='张三' , age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0' , updated='2020-06-17 09:30:58.0' , interests=null} ,
可以看到,在两次查询 SQL 语句中使用插入 SQL 语句,会对一级缓存进行刷新,会导致一级缓存失效。
18.3.2.3不同的SqlSession的 我们知道一级缓存就是 SqlSession 级别的缓存,而同一个 SqlSession 会有相同的一级缓存,那么使用不同的 SqlSession 是不是会对一级缓存产生影响呢?
@Test
public void cacheTest ( ) {
List < UserEntity > userEntities = userMapper. selectUserByAge ( 20 ) ;
System . out. println ( userEntities) ;
UserMapper userMapper2
= sqlSessionFactory. openSession ( ) . getMapper ( UserMapper . class ) ;
List < UserEntity > userEntities2 = userMapper2. selectUserByAge ( 20 ) ;
System . out. println ( userEntities2) ;
执行测试,结果如下:
2020-08-10 16:26:36, 243 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Preparing: select * from tb_user where age > ?
2020-08-10 16:26:36, 290 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Parameters: 20( Integer)
2020-08-10 16:26:36, 322 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] <== Total: 7
[ UserEntity{ id=1, userName='张三' , password='123456' , name='张三' , age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0' , updated='2020-06-17 09:30:58.0' , interests=null}
2020-08-10 16:26:36, 337 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Preparing: select * from tb_user where age > ?
2020-08-10 16:26:36, 337 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] ==> Parameters: 20( Integer)
2020-08-10 16:26:36, 353 [main] [mapper.UserMapper.selectUserByAge] - [DEBUG] <== Total: 7
[ UserEntity{ id=1, userName='张三' , password='123456' , name='张三' , age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0' , updated='2020-06-17 09:30:58.0' , interests=null}
上面代码使用了不同的 SqlSession 对同一个 SQL 语句执行了相同的查询操作,却对数据库执行了两次相同的查询操作,生成了不同的 UserEnity 对象,由此可见,不同的 SqlSession 是肯定会对一级缓存产生影响的。
18.3.2一级缓存的问题 当有两个 SqlSession 对象存在,一个用于查询数据,一个用于更新数据,如果查询和更新是同一张表的相同数据,这时可能会出现数据脏读。
例如,假设有两个 SqlSession
对象 A 和 B。A 执行了一个查询语句并缓存了查询结果。然后 B 执行了一个更新语句并提交了更改。此时,如果 A 再次执行相同的查询语句,它将直接从缓存中获取结果,而不是再次查询数据库。这意味着 A 获取到的数据可能是过时的,因为它没有感知到 B 所做的更改。
而解决办法是清空缓存。
mybatis :
configuration :
cache-enabled : false
local-cache-scope : statement
18.2Mybatis二级缓存 18.2.1二级缓存概述
二级缓存是 Mapper 级别的缓存。
多个 SqlSession
对象 SQL 语句查询数据库结果会存放二级缓存区域,而多个SqlSession
对象可以共用二级缓存。
二级缓存是多个 SqlSesion
对象共用的。
其作用范围是Mapper
的同一个 namespace
,不同的 SqlSession
对象再次执行相同 namepace
下的 SQL 语句,第一次执行会将数据库中查询结果数据存储到二级缓存中,第二次会从二级缓存中获取数据,而不再从数据库中获取,从而提高查询效率。
MyBatis
二级缓存默认关闭,需要手动开启二级缓存。
MyBatis 的二级缓存是 Mapper 范围级别,除了在 MyBatis 环境配置mybatis-config.xml
设置二级缓存总开关,还要在具体的mapper.xml
中加入 标签。
18.2.2二级缓存配置
在核心配置文件中,设置全局配置属性cacheEnabled=”true”,默认为true,不需要设置
< settings>
< ! -- 缓存,默认也是开启-- >
< ! -- 有些二级缓存可能用到序列化技术,所以entity类要Serializable 接口-- >
< setting name= "cacheEnabled" value= "true" / >
< / settings>
配置完毕之后,找到相对应想要做二级缓存的XXMapper.xml文件,在 中添加<cache/>
标签:
< cache eviction= "FIFO" flushInterval= "60000" size= "512" readOnly= "true" / >
配置中参数信息含义如下:
eviction : 缓存的回收策略
flushInterval : 时间间隔(毫秒)
size : 引用数目,缓存对象数目和运行环境数目,默认1024
readOnly : 是否只读
其中,对于eviction的回收策略,有其四种:
LRU - 最近最少使用:移出最长时间不被使用的对象 (默认);
FIFO - 先进先出(队列):按对象进入缓存的顺序移除 ;
SOFT - 软引用: 移除基于垃圾回收器状态和软引用规则的对象 ;
WEAK - 弱引用:更积极移除基于垃圾收集器状态和引用规则的对象 。
18.2.3如何禁用二级缓存
给mybatis-config.xml添加如下属性时,将全局关闭缓存;
< settings>
< setting name= "cacheEnabled" value= "false" / >
< / settings>
不在不需要进行缓存的Mapper.xml添加 ;
给对应SQL语句进行设置局部禁用缓存(userCache) 例
< select id= "getSomeThing" parameterType= "Integer" resultType= "Integer" useCache= "false" >
SELECT * FROM `table`
< / select>
18.3缓存说明
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
19.MyBatis的逆向工程
什么是逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程
的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
Mapper映射文件
创建逆向工程的步骤
一:清新简洁版
添加相关依赖和插件
< dependencies>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
< version> 4.13.1</ version>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> org.mybatis</ groupId>
< artifactId> mybatis</ artifactId>
< version> 3.5.9</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 8.0.19</ version>
</ dependency>
< dependency>
< groupId> log4j</ groupId>
< artifactId> log4j</ artifactId>
< version> 1.2.12</ version>
</ dependency>
</ dependencies>
< build>
< plugins>
< plugin>
< groupId> org.mybatis.generator</ groupId>
< artifactId> mybatis-generator-maven-plugin</ artifactId>
< version> 1.3.0</ version>
< dependencies>
< dependency>
< groupId> org.mybatis.generator</ groupId>
< artifactId> mybatis-generator-core</ artifactId>
< version> 1.3.2</ version>
</ dependency>
< dependency>
< groupId> com.mchange</ groupId>
< artifactId> c3p0</ artifactId>
< version> 0.9.2</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 8.0.19</ version>
</ dependency>
</ dependencies>
</ plugin>
</ plugins>
</ build>
创建逆向工程的配置文件
< ? xml version= "1.0" encoding= "UTF-8" ? >
< ! DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
< generatorConfiguration>
< ! -- targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple : 生成基本的CRUD (清新简洁版)
MyBatis3 : 生成带条件的CRUD (奢华尊享版) -- >
< context id= "DB2Tables" targetRuntime= "MyBatis3Simple" >
< ! -- 数据库的连接信息 -- >
< jdbcConnection driverClass= "com.mysql.cj.jdbc.Driver"
connectionURL= "jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"
userId= "root"
password= "root" >
< / jdbcConnection>
< ! -- javaBean的生成策略-- >
< javaModelGenerator targetPackage= "com.pdsu.mybatis.pojo" targetProject= ".\src\main\java" >
< property name= "enableSubPackages" value= "true" / >
< property name= "trimStrings" value= "true" / >
< / javaModelGenerator>
< ! -- SQL 映射文件的生成策略 -- >
< sqlMapGenerator targetPackage= "com.pdsu.mybatis.mapper" targetProject= ".\src\main\resources" >
< property name= "enableSubPackages" value= "true" / >
< / sqlMapGenerator>
< ! -- Mapper 接口的生成策略 -- >
< javaClientGenerator type= "XMLMAPPER" targetPackage= "com.pdsu.mybatis.mapper"
targetProject= ".\src\main\java" >
< property name= "enableSubPackages" value= "true" / >
< / javaClientGenerator>
< ! -- 逆向分析的表 -- > < ! -- tableName设置为* 号,可以对应所有表,此时不写domainObjectName -- >
< ! -- domainObjectName属性指定生成出来的实体类的类名 -- >
< table tableName= "t_employee" domainObjectName= "Employee" / >
< table tableName= "t_dept" domainObjectName= "Dept" / >
< / context>
< / generatorConfiguration>
执行项目插件的generate目标
执行结果
可以看出已经自动生成mapper接口、实体类和实体类映射文件
二:奢华尊享版
和清新简洁版不同的是,奢华尊享版需要将generatorConfig配置文件中的
targetRuntime属性值更换为MyBatis3
执行项目插件的generate目标
其中mapper接口中有很多方法
二:QBC查询
什么是QBC查询:QBC即Quary By Criteria,Criteria是Criterion的复数,译为规则,准则,在sql语句 中相当于查询条件。QBC查询是将查询条件通过Java对象进行模块化封装。
测试:
@Test
public void test1 ( ) {
try {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
EmployeeMapper mapper = sqlSession. getMapper ( EmployeeMapper . class ) ;
EmployeeExample example = new EmployeeExample ( ) ;
example. createCriteria ( ) . andENameEqualTo ( "枫原万叶" ) ;
List < Employee > employees = mapper. selectByExample ( example) ;
employees. forEach ( System . out:: println ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
20.分页插件
配置分页插件,加入相应依赖和插件配置
< dependency>
< groupId> com.github.pagehelper</ groupId>
< artifactId> pagehelper</ artifactId>
< version> 5.2.0</ version>
</ dependency>
分页插件的使用
在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
pageNum:当前页的页码
pageSize:每页显示的条数
@Test
public void test2 ( ) {
try {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
EmployeeMapper mapper = sqlSession. getMapper ( EmployeeMapper . class ) ;
PageHelper . startPage ( 1 , 3 ) ;
List < Employee > employees = mapper. selectByExample ( null ) ;
employees. forEach ( System . out:: println ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
即打印第一页,并且显示3条数据
打印第二页,并且显示3条数据
在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据
list:分页之后的数据
navigatePages:导航分页的页码数
@Test
public void test2 ( ) {
try {
InputStream is = Resources . getResourceAsStream ( "mybatis-config.xml" ) ;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( is) ;
SqlSession sqlSession = sqlSessionFactory. openSession ( true ) ;
EmployeeMapper mapper = sqlSession. getMapper ( EmployeeMapper . class ) ;
PageHelper . startPage ( 2 , 3 ) ;
List < Employee > employees = mapper. selectByExample ( null ) ;
PageInfo < Employee > pageInfo = new PageInfo < > ( employees, 3 ) ;
System . out. println ( pageInfo) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
PageInfo对象
PageInfo { pageNum= 2 , pageSize= 3 , size= 3 ,
startRow= 4 , endRow= 6 , total= 8 , pages= 3 ,
list= Page { count= true , pageNum= 2 , pageSize= 3 ,
startRow= 3 , endRow= 6 , total= 8 , pages= 3 ,
reasonable= false , pageSizeZero= false }
[ Employee { eid= 4 , eName= '雷电将军' , did= 2 } ,
Employee { eid= 5 , eName= '班尼特' , did= 1 } ,
Employee { eid= 6 , eName= '行秋' , did= 1 } ] ,
prePage= 1 , nextPage= 3 , isFirstPage= false ,
isLastPage= false , hasPreviousPage= true ,
hasNextPage= true , navigatePages= 3 ,
navigateFirstPage= 1 , navigateLastPage= 3 ,
navigatepageNums= [ 1 , 2 , 3 ] }
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
21.MyBatis的原理 21.1JDBC概述 JDBC(Java Database Connectivity)是Java中用于执行SQL语句的API,它提供了一种访问多种关系数据库的方法。
Java JDBC API提供以下接口和类 。
接口:
Connection
:特定数据库的连接(会话)。在连接上下文中执行SQL语句并返回结果。
PreparedStatement
:表示预编译的 SQL 语句的对象。
Statement
:用于执行静态 SQL 语句并返回它所生成结果的对象。
ResultSet
:表示数据库结果集的数据表,通常通过执行查询数据库的语句生成 。
CallableStatement
:用于执行 SQL 存储过程的接口 。
类:
DriverManager
:负责管理JDBC驱动程序。使用JDBC驱动程序之前,必须先将驱动程序加载并注册后才可以使用,同时提供方法来建立与数据库的连接。(加载到内存中才能使用)
SQLException
:有关数据库操作的异常。
21.2JDBC的执行流程 21.2.1说明
载入JDBC驱动程序
定义连接URL
建立连接
创建Statement对象
执行查询或更新
结果处理
关闭连接
21.2.2Connection
Connection connection = DriverManager. getConnection(
"jdbc:mysql://127.0.0.1:3306/data?user=root&password=123456" ) ;
DataSource dataSource = new UnpooledDataSource (
"com.mysql.cj.jdbc.Driver" ,
"jdbc:mysql://127.0.0.1:3306/data?user=root&password=123456&AllowPublicKeyRetrieval=true" ,
"root" , "qwer1234" ) ;
21.2.3Statement
Statement 不支持输入参数,有sql注入的风险
PreparedStatement:增加了设置SQL参数的方法
CallableStatement:增加了调用存储过程以及检索存储过程调用结果的方法
Connection connection = DriverManager . getConnection ( "" ) ;
String sql = "SELECT * FROM admin WHERE username = ? AND password = ?" ;
PreparedStatement preparedStatement = connection. prepareStatement ( sql) ;
preparedStatement. setString ( 1 , "username" ) ;
preparedStatement. setString ( 2 , "password" ) ;
ResultSet resultSet = preparedStatement. executeQuery ( ) ;
String sql2 = "SELECT * FROM admin WHERE username = 'username' AND password = 'password'" ;
Statement statement2 = connection. createStatement ( ) ;
ResultSet resultSet2 = statement. executeQuery ( sql) ;
21.3MyBatis的原理 MyBatis
的基本工作原理就是:先封装SQL,接着调用JDBC操作数据库,最后把数据库返回的表结果封装成Java类。
MyBatis
的工作流程包括以下几个步骤:
加载Mybatis的配置文件。
根据配置文件信息,使用SqlSessionFactoryBuilder()
来创建一个sqlSessionFactory
对象。
使用sqlSessionFactory
会话工厂的openSqlSession
来创建一个SqlSession对象。该对象中包含了执行SQL语句的所有方法。
在sqlSession
对象内部调用Executor
执行器,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句。首先会根据此sql语句去缓存中查找,如果缓存中没有值,就调用doQuery
方法从数据库中查询数据。
在doQuery()
方法中创建一个StatementHandler
对象,然后将必要的参数传递给StatementHandler
,使用StatementHandler
来创建Statement
对象,将SQL语句发送到数据库中,完成对数据库的查询操作,最终返回List结果集。
最后使用resultSetHandler
对结果集进行处理,完成输出结果映射。
22.MyBatis的插件的原理 22.1MyBatis的插件概述 MyBatis的插件本身就是一个拦截器。
22.2MyBatis插件实现
实现MyBatis提供的Interceptor
接口,重写其中的interceptor
方法、plugin
方法和setProperties
方法。
然后在实现插件接口的类上添加@Intercepts
注解指定要拦截哪一个接口的哪些方法,最后在配置文件中配置自定义插件。
22.3MyBatis插件的实现原理 MyBatis插件的本质就是拦截器。MyBatis 仅可以编写针对 ParameterHandler
、Executor
、StatementHandler
、ResultSetHandler
这 4 种接口的插件,MyBatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler
的 invoke()
方法来执行拦截方法的自身逻辑。
ParameterHandler (参数处理器):首先执行,用于处理参数。 - 适合拦截 SQL 语句执行前的操作,例如修改或验证参数。 - 如果你需要在 SQL 语句执行之前对参数进行操作或验证,可以选择拦截 ParameterHandler。
StatementHandler (语句处理器):在参数处理器之后执行,用于处理 SQL 语句的生成和处理。 - 适合拦截 SQL 语句的生成和处理。 - 如果你需要动态修改生成的 SQL 语句,或者修改 Statement 对象的行为,可以选择拦截 StatementHandler。
Executor (执行器):在语句处理器之后执行,用于执行 SQL 语句。 - 适合拦截 SQL 语句的执行,包括查询、更新、删除等操作。 - 如果你需要在 SQL 语句执行前后执行一些额外的逻辑,或者记录执行日志、计算执行时间等,可以选择拦截 Executor。
ResultSetHandler (结果集处理器):最后执行,用于处理查询结果集。 - 适合拦截结果集的处理,通常用于自定义结果集的映射或处理。 - 如果你需要自定义结果集的处理逻辑,可以选择拦截 ResultSetHandler。