目录
3.若mapper接口方法的参数有多个时,可以手动将这些参数放在mapper中存储
4.如果mapper接口方法的参数是实体类类型的参数(重点)
5.使用@Param注解(最常用的方式,除了第三种情况以外,其他情况都可以用它)
若字段名和实体类中的属性基本一致,只需要下划线和驼峰互相转换
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
方法一:直接输出pageHelper.startPage的值
一.MyBatis简介
- MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github
- iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
1.Java学习流程
2.MyBatis特性
(1).MyBatis是支持定制化SQL,存储过程以及高级映射的持久层框架
(2).MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
(3).MyBatis可以使用简单的xml或注解方式用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的java对象)映射成数据库中的记录
(4)MyBatis是一个半自动的ORM(Object Relation Mapping)框架
3.Mybatis的下载
MyBatis下载地址:https://github.com/mybatis/mybatis-3
4.MyBatis和其他持久化层技术对比
- JDBC
- SQL夹杂在Java代码中耦合度高,导致硬编码(代码写死)内伤
- 维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate和JPA
- 操作简单,开发效率高
- 程序中的长难复杂SQL需要绕过框架(简单的增删改插不需要)
- 内部自动生成的SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段的POJO(JavaBean类)进行部分映射时会比较困难(想查一个表中的单独几个字段不容易)
- 反射操作太多,导致数据库性能下
- MyBatis
- 轻量级,性能出色
- SQL和Java编码分开,功能边界清晰。JAva代码专注业务,SQL语句专注数据
- 开发效率稍逊于HIbernate,但是完全能够接受
二.搭建MyBatis
1.开发环境
IDEA:idea 2019.3
构建工具:maven 3.5.4
MySQL版本:MYSQL 8.0.2
MyBAtis版本:MyBatis 3.5.7
2.创建maven工程
(1)创建maven父工程
-
第一步选中maven工程创建
-
输入你的ArtifactId和groupId(项目唯一标识符,坐标)
-
点击finish,Maven父工程创建成功
-
设置项目Maven,点击Maven右边的小扳手就可以打开设置Maven仓库和setting配置文件,Maven下载依赖的时候先去寻找本地仓库有没有,没有再去自己setting配置中的镜像网站下载,没配置直接中央仓库下载,配置好点击apply
-
创建子工程(在这里面编写自己的模块功能)
-
配置打包方式,packaging为jar包表明是一个jar工程
-
导入依赖
<dependencies> <!-- Mybatis核心 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <!-- junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
父工程的最要作用就是管理子工程jar依赖的版本问题,在父工程导入的依赖,在子工程中使用就不需要在编写版本号
3.创建MyBatis的核心配置文件
习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作可以直接复制,粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
(1)创建一个xml文件
(2)代码段复制进去
<?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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
4.创建Mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类
(1)数据库student表和实体类
(2) Mapper接口创建
5.创建MyBatis的映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射
- 对象:java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
Java概念 | 数据库概念 |
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
1.映射文件的命名规则
- 表所对应的实体类的类名+Mapper.xml
- 列如:表t_user,映射的实体类为User,所对应的接口UserMapper,对应的映射文件名为UserMapper.xml
- 因此一个映射文件对应一个实体类,对应一张表的操作
- MyBatis映射文件用于编写SQL,访问以及操作表中的数据
2.MyBatis映射文件存放的位置是src/main/resources/mappers目录下
- mapper接口中的全类名和映射文件的命名空间(namespace)保持一致
- mapper接口中的方法的方法名和映射文件中编写SQL的标签的id属性保持一致
3.MyBatis面向接口的两个一致和对应关系
MyBatis面向接口的两个一致 * 1.映射文件的namespace要和mapper接口的全类名一致 * 2.映射文件中SQL语句的id要和mapper接口中的方法名一致 * * 对应关系 * 表--实体类--mapper接口--xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cn.cslg.mybatis.mapper.UserMapper">
<!-- sql语句方法写入 int insertUser(); -->
<insert id="insertUser">
insert into t_user values (null,'admin','123456',23,'男','123.@qq.com')
</insert>
</mapper>
Mapper核心配置文件引入Mapper映射文件
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
6.通过juint测试功能
- SqlSession:代表Java程序和数据库之间的会话,(HTTPSession是Java程序和浏览器之间的会话)
- SqlSessionFactory:是“生产”SqlSession的“工厂”
- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们可以把创建这个对象的相关代码封装到一个“工厂类”中,以后使用这个工厂类来“生产”我们需要的对象
package com.cn.cslg.mybatis.test;
import com.cn.cslg.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisTest {
/**
* SqlSession默认不自动提交事务,若需要自动提交事务
* 可以使用sqlSessionFactoryp。open(true)
*
*/
@Test
public void testMyBatisInsertUser() throws IOException {
//1.加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//2.获取SqlSessionFactoryBuilder,是SqlSession工厂对象的构建对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3.获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
//4.获取SqlSession,是java和数据库之间的会话,就像httpSession是浏览器和java之间的会话,设置为true开启自动提交事务
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//测试功能
mapper.insertUser();
//提交事务
// sqlSession.commit();
}
}
7.加入log4j日志功能
1.加入依赖
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.加入log4j的配置文件
log4j的配置文件名为log4j.xml,存放的位置为src/main/resources目录下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!-- 命名空间爆红没有关系,不影响使用,下面的level标签中是指的输出范围 -->
<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>
日志的级别
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
三.核心配置文件详解
核心配置文件中的标签必须按照固定的顺序
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
<?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:设置默认使用的环境的id
-->
<environments default="development">
<!--
environment:配置某个具体的环境
属性:
id:表示连接数据库的环境的唯一标识,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理方式
属性:
type="JDBC|MANAGED"
JDBC:表示当前环境中,执行SQL时,使用的事JDBC中原生的事务管理方式,事物的提交和回滚需要手动操作
MANAGED:别管理,列如Spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource:配置数据源的类型
type="POOLED|UNPOOLED|NDI"
POOLED:表示使用数据库连接池缓存数据库链接
UNPOOLED:表示不适用竖亥句酷连接池
JNDI:表示使用上下稳重的数据源
-->
<dataSource type="POOLED">
<!-- 设置链接数据库的驱动 -->
<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="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
TypeAliases标签
typeAliases用来设置类名的别名,通常使用typeAliases下面的package标签设置一整个实体类的包的别名,在Mybatis核心文件中的配置
<!--
typeAlias:设置某个类型的别名
属性:
type:设置需要设置别名的类型
alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名不区分大小写
-->
<typeAliases>
<typeAlias type="com.cn.cslg.mybatis.pojo.User" alias="User"></typeAlias>
<!-- 以包为单位,将包下所有的类型设置默认的类型别名, 且类名不区分大小写 -->
<package name="com.cn.cslg.mybatis.pojo"/>
</typeAliases>
在Mapper映射文件中的使用类名的别名,resultType中的User的首字母大小写不用区分
<select id="selectAllUser" resultType="User">
select * from t_user
</select>
<select id="selectAllUser" resultType="user">
select * from t_user
</select>
Package标签映入多个映射文件
包的方式映入Mapper映射文件,解决了配置多个包的问题,其中需要注意在resourc文件夹下面创建多个目录需要用‘/’分割而不是用'.'分隔。
- 首先必须mapper接口所在的包要和映射文件所在的包一致
- mapper接口要和映射文件的名字一致
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml"/>-->
<!--
已包为单位映入映射文件
要求:
1.mapper接口所在的包要和映射文件所在的包一致
2.mapper接口要和映射文件的名字一致
-->
<package name="com.cn.cslg.mybatis.mapper"/>
</mappers>
四.MyBatis的增删改插
1.添加
<insert id="insertUser">
insert into t_user values (null ,'admin','123456',23,'男','123.@qq.com')
</insert>
2.删除
<delete id="deleteUser">
delete from t_user where id ='3'
</delete>
3.修改
<update id="updateUser">
update t_user set username = 'zkkk' where id=1
</update>
4.查询实体类对象
查询实体类的对象需要设置resultType为实体类返回值,可以通过TypeAliases在MyBatis核心配置文件中通过TypeAliases标签来更改类的别名
<!-- User selectUser();-->
<!--
查询功能的标签必须设置resultType或者resultMap
resultType:设置默认的映射关系
resultMap:设置自定义的映射关系
-->
<select id="selectUser" resultType="com.cn.cslg.mybatis.pojo.User">
select * from t_user where id = 1
</select>
5.查询多个实体类对象
通过更改类的别名来,在resultType中简单的命名,因为通过package标签进行多个映射文件导入,所以别名即为实体类的类名不用区分大小写!
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml"/>-->
<!--
已包为单位引入映射文件
要求:
1.mapper接口所在的包要和映射文件所在的包一致
2.mapper接口要和映射文件的名字一致
-->
<package name="com.cn.cslg.mybatis.mapper"/>
</mappers>
<!-- List<User> selectAllUser();-->
<select id="selectAllUser" resultType="User">
select * from t_user
</select>
注意:
1.查询的标签select必须设置属性resultType或resultMap ,用于设置实体类和数据库的映射关系
resultType:自动映射,用于属性名和标准字段名一致的情况
resultMap:自定义映射,用于一对多或多对一字段名和属性名不一致的情况
2.当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合为返回值
五.MyBatis获取参数值的两种方式(重点)
- MyBatis获取参数值的两种方式:${}和#{}
- ${}本质字符串拼接,拼接的值为字符串类型或者日期类型,需要注意单引号问题
- #{}本质占位符赋值
MyBatis获取参数值的各种情况
1.mapper接口方法的参数为单个字面量类型
-
可以通过${}和#{}以任意的名称获取,最好是见名识意,但是需要注意${}的单引号问题
<!--User getUserByUserName(String UserName);-->
<select id="getUserByUserName" resultType="User">
<!--select * from t_user where username = #{UserName}-->
<!-- 注意单引号-->
select * from t_user where username = '${UserName}'
</select>
2.mapper接口方法的参数为多个时
- 此时MyBatis会将这些参数放在map集合中,以两种方式进行存储
- a>以arg0,arg1...为键,以参数为值
- b>以param1,param2...为键,以参数为值
- 因此只需要通过#{}或者${}以键的方式访问值即可,注意${}的引号问题,l例子#{arg0}
<!--User checkLogin(String username,String password);-->
<select id="checkLogin" resultType="User">
<!-- select * from t_user where username=#{arg0} and password=#{arg1}-->
select * from t_user where username=#{param1} and password=#{param2}
</select>
3.若mapper接口方法的参数有多个时,可以手动将这些参数放在mapper中存储
因此只需要通过#{}或者${}以键的方式访问值即可,注意${}的引号问题,l例子#{username}
public void testGetAllUser(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
//传输多个参数为mapper集合的时候判断登录
Map<String,Object> map = new HashMap<>();
map.put("username","zkkk");
map.put("password","123456");
User user = mapper.checkLoginByMap(map);
System.out.println(user);
}
<!--User checkLoginByMap(Map<String,Object>map);-->
<select id="checkLoginByMap" resultType="User">
select * from t_user where username=#{username} and password=#{password}
</select>
4.如果mapper接口方法的参数是实体类类型的参数(重点)
因此只需要通过#{}或者${}以属性的方式访问值即可,注意${}的引号问题,l例子#{类中的属性}
package com.cn.cslg.mybatis.pojo;
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String email;
....
}
<!--int insertUser(User user);-->
<insert id="insertUser">
insert into t_user values(null ,#{username},#{password},#{age},#{sex},#{email})
</insert>
public void insertUser(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
User user =new User(null,"Lisi","123456",12,"男","123456");
int a = mapper.insertUser(user);
System.out.println(a);
}
5.使用@Param注解(最常用的方式,除了第三种情况以外,其他情况都可以用它)
此时MyBatis会将这些参数放在一个map集合之中,以两种方式进行存储 a>@Param注解的值为键,以参数为值 b>以param1,param2...为键,以参数位置 因此只需要通过#{}和${}以键的方式访问值即可,但是需要注意${}的单引号问题
public void testCheckLoginByParam(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
User user = mapper.checkLoginByParam("zkkk", "123456");
System.out.println(user);
}
/**
* mapper接口
* 验证登录(使用@param注解)
*/
User checkLoginByParam(@Param("username") String username,@Param("password") String password);
<!--User checkLoginByParam(@Param("username") String username,@Param("password") String password);-->
<select id="checkLoginByParam" resultType="User">
select * from t_user where username=#{username} and password=#{password}
</select>
总结
- 所有获取传传递值的情况可以分为两种
- a>实体类型类型参数
- b>使用@Param标识参数
六.MyBatis的各种查询功能
1、查询一个实体类对象
1.若查询出的数据只有一条 * a>可以通过实体类对象收 * b>可以通过list集合接收 * c>可以通过Map集合来接收
/**
* 根据id查询用户信息
*/
User getUserId(@Param("Uid") Integer id);
<!--User getUserId(@Param("Uid") Integer id);-->
<select id="getUserId" resultType="User">
select * from t_user where id=#{Uid}
</select>
2、查询一个list集合
可以通过设置返回值为List<User>
/**
* 查询所有的用户信息
*/
List<User> getAllUser();
<!--List<User> getAllUser();-->
<select id="getAllUser" resultType="User">
select * from t_user
</select>
3、查询单个数据
/** MyBatis中设置了默认的类型别名
* Java.lang.Integer-->int,integer
* int-->_int,_integer
* Map-->map
* String-->string
*/
/**
* 查询用户信息总记录数
*/
Integer getCount();
<!--Integer getCount();-->
<!--resultType 也可以写Integer integer int Int 类型别名不区分大小写-->
<select id="getCount" resultType="java.lang.Integer">
select count(*) from t_user
</select>
4、查询一条数据为map集合
/**
* 根据id查询用户信息为一个map集合
*/
Map<String,Object>getUserByIdToMap(@Param("id") Integer id);
<!--Map<String,Object>getUserByIdToMap(@Param("id") Integer id);-->
<select id="getUserByIdToMap" resultType="map">
select * from t_user where id=#{id}
</select>
结果:{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}
5、查询多条数据为map集合
方法一 用list集合包含map集合
List<Map<String,Object>>getAllUserByListMap();
<!--List<Map<String,Object>>getAllUserByListMap();-->
<select id="getAllUserByListMap" resultType="map">
select * from t_user
</select>
重点
注意此时的resultType必须为map对象,因为查询的所有user对象被一一存入map中然后将每个map在存放在list集合中
如果返回值是user的话,那么你会发现他其实list存放的不是多个包含User的map集合,而仅仅是多个User对象存储在list中,并未存放在map中
public void getAllUserByListMap(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
List<Map<String, Object>> allUserByListMap = mapper.getAllUserByListMap();
System.out.println(allUserByListMap);
System.out.println(allUserByListMap);
//System.out.println(allUserByListMap.get(0));
for (Map<String,Object> map: allUserByListMap){
System.out.println(map);
System.out.println(map.get("id"));
}
}
输出结果
[{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}, {password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}, {password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}, {password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}, ]
{password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}
1
{password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}
4
{password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}
7
{password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}
8
如果是resultType返回值是User的话,输出内容,我们通过获得list集合中每一个元素map的key判断list集合中的元素为map集合
<!--List<Map<String,Object>>getAllUserByListMap();-->
<select id="getAllUserByListMap" resultType="user">
select * from t_user
</select>
他输出了所有的内容但是会报一个没有key的错误,同时从输出上也能看出来这是一个list集合中包含着多个User对象
@Test
public void getAllUserByListMap(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
List<Map<String, Object>> allUserByListMap = mapper.getAllUserByListMap();
//System.out.println(allUserByListMap);
System.out.println(allUserByListMap);
System.out.println(allUserByListMap.get(0).get("id"));
//for (Map<String,Object> map: allUserByListMap){
// System.out.println(map);
// System.out.println(map.get("id"));
//
//}
}
结果
[User{id=1, username='zkkk', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=4, username='admin', password='123456', age=23, sex='男', email='1281628436@qq.com'}, User{id=7, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=8, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=9, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=10, username='admin', password='123456', age=23, sex='男', email='123.@qq.com'}, User{id=11, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=12, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=13, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=14, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=15, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=16, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=17, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=18, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=19, username='Lisi', password='123456', age=12, sex='男', email='123456'}, User{id=20, username='Lisi', password='123456', age=12, sex='男', email='123456'}]
java.lang.ClassCastException: com.cn.cslg.mybatis.pojo.User cannot be cast to java.util.Map
方法二通过注释@mapkey来直接返回多个不同的map
可以在mapper接口的方法上添加@MapKey注解,此时就可以将每条数据转换的map集合作为值,以某个字段的值作为键,比如id,放在同一个map集合里面
/**
* 查询所有用户信息为map集合
*/
@MapKey("id")
Map<String,Object> getAllUserToMap();
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
@Test
public void getUserByIdMap(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
Map<String, Object> userByIdToMap = mapper.getUserByIdToMap(1);
//System.out.println(userByIdToMap.get("username"));
System.out.println(mapper.getUserByIdToMap(1));
System.out.println(mapper.getAllUserToMap());
}
输出结果:
{1={password=123456, sex=男, id=1, age=23, email=123.@qq.com, username=zkkk}, 4={password=123456, sex=男, id=4, age=23, email=1281628436@qq.com, username=admin}, 7={password=123456, sex=男, id=7, age=23, email=123.@qq.com, username=admin}, 8={password=123456, sex=男, id=8, age=23, email=123.@qq.com, username=admin}, }
七. 特殊SQL的执行
1.模糊查询
/**
* 根据用户名模糊查询用户信息
*/
List<User> getUserByLike(@Param("username") String username);
<!--List<User> getUserByLike(@Param("username") String username);-->
<select id="getUserByLike" resultType="User">
<!--占位符方法不行,下面三种都可以,推荐23两种
select * from t_user where username like '%#{username}%'-->
<!--select * from t_user where username like '%${username}%'-->
<!--select * from t_user where username like concat('%',#{username},'%')-->
select * from t_user where username like "%"#{username}"%"
</select>
2.批量删除
/**
* 批量删除
*/
int deleteMore(@Param("ids") String ids);
<!--int deleteMore(@Param("ids") String ids);-->
<delete id="deleteMore">
<!--现实中为了防止sql注入通常使用foreach循环删除-->
delete from t_user where id in (${ids})
</delete>
@Test
public void testDeleteMore(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
int i = mapper.deleteMore("10,11,12");
System.out.println(i);
}
3.动态设置表名
/**
* 查询指定表中的数据
*/
List<User> getUserByTableName(@Param("tableName") String tableName);
<!--List<User> getUserByTableName(@Param("tableName") String tableName);-->
<select id="getUserByTableName" resultType="User">
select * from ${tableName}
</select>
4.添加功能获取自增主键
t_clazz(cd,cname)
t_student(sid,sname,cid)
1.添加班级信息
2.获取新添加班级的id
3.为班级分配学生,将某个学生的班级id修改为新添加的班级id
userGeneratedKeys:设置当前便签中的sql使用了自增的id
keyPorperty:将自增的主键的值付给传输到映射文件中参数的某个属性
属性并不是在类中定义了才算,只要有get和set方法都算属性
/**
* 添加用户信息
*/
void insertUser(User user);
<!--
void insertUser(User user);
userGeneratedKeys:设置当前标签中的sql使用了自增的id
keyProperty:将自增的主键的值赋值给传输到映射文件中参数的某个属性
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>
八.自定义映射resultMap
1.resultMap处理字段和属性的映射关系
若字段名和实体类中的属性基本一致,只需要下划线和驼峰互相转换
a>为字段起别名,保持和属性名一致
<select id="getAllEmp0" resultType="Emp"> <!--设置别名方法解决字段名和属性名不一致的情况,在字段名后面空格打上属性名--> select eid,emp_name empName,age,sex,email from t_emp </select>
b>设置全局配置,将_自动映射为驼峰
<setting name="mapUnderscoreToCamelCase" value="true"/>
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
resultMap:设置自定义映射关系
- 属性
- id:唯一标识,不能重复
- type:设置映射关系中的实体类类型
- 子标签:
- id:设置主键的映射关系
- result:设置普通字段的映射关系
- 子标签属性:
- property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
- colum:设置映射关系中的字段名,必须是sql语句查询出的字段名
<!--
resultMap:设置自定义映射关系
id:唯一标识,不能重复
type:设置映射关系中的实体类类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
属性:
property:设置映射关系中的属性名,必须是type属性所设置的实体类类型中的属性名
colum:设置映射关系中的字段名,必须是sql语句查询出的字段名
-->
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--<result property="did" column="did"></result>-->
</resultMap>
2.多对一映射处理
查询所有员工信息包括其所对应的部门信息
//员工表
public class Emp {
private Integer eid;
private String empName;
private Integer age;
private String sex;
private String email;
private Dept dept;
}
//部门表
public class Dept {
private Integer did;
private String deptName;
}
a>级联属性赋值
<!--处理多对一映射关系方式1:级联属性赋值-->
<!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
<resultMap id="empAndDeptResultMapOne" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<result property="dept.did" column="did"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
b>association
association:处理多对一的映射关系
property:需要处理的属性名
JavaType:该属性的类型
<!--处理多对一映射关系方式2:association-->
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
association:处理多对一的映射关系
property:需要处理的属性名
JavaType:该属性的类型
-->
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
c>分布查询
首先查出员工的所有信息,通过员工信息里面的did在查出员工所属部门信息
1.先查出员工的所有信息
2.通过did参训员工所对应的部门
/**
* 通过分布查询员工以及员工所对应的部门信息
* 分布查询第一步:查询员工信息
*/
Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);
select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)
colum:设置分布查询的条件,例子是根据员工表中的did属性查询所属部门信息
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<resultMap id="getEmpAndDeptByStep" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)
column:设置分布查询的条件
-->
<association property="dept"
select="com.cn.cslg.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
></association>
</resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStep">
select * from t_emp where eid=#{eid}
</select>
/**
* 通过分布查询员工以及员工所对应的部门信息
* 分布查询第二步:通过did参训员工所对应的部门
*/
Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);
<!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where did=#{did}
</select>
测试结果和输出
@Test
public void testGetEmpAndDeptByStep(){
SqlSession session = SqlSessionUtils.getSession();
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
System.out.println(empAndDeptByStepOne);
}
结果:
分布查询会执行两次sql语句
DEBUG 06-23 22:55:46,980 ==> Preparing: select * from t_emp where eid=? (BaseJdbcLogger.java:137)
DEBUG 06-23 22:55:47,018 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-23 22:55:47,050 ====> Preparing: select * from t_dept where did=? (BaseJdbcLogger.java:137)
DEBUG 06-23 22:55:47,051 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-23 22:55:47,056 <==== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 06-23 22:55:47,057 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=Dept{did=1, deptName='A'}}
分布查询的优点:延迟加载
分布查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息;
lazyLoadingEnable:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggresslveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否者,每个属性按需要加载。
此时就可以实现按需加载,获取的数据是什么,就是会执行相对应的sql语句。此时通过association和collection中国的fecthType属性设置当前的分布查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”
<!--设置MyBatis的全局配置-->
<settings>
<!--将_自动映射为驼峰,emp_name:empName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnable" value="true"/>
</settings>
<!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
<resultMap id="getEmpAndDeptByStep" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<!--
select:设置分布查询的sql的唯一表示(namespace.SQLID或mapper接口的全类名,方法名)
column:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过此属性手动控制延迟加载的效果
fetchType:"Lazy|eager":lazy表示延迟加载,eager表示立即加载
-->
<association property="dept"
select="com.cn.cslg.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy">
</association>
</resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStep">
select * from t_emp where eid=#{eid}
</select>
fetchType:当开启了全局的延迟加载之后 ,可以通过属性手动控制延迟加载的效果,前提是在mybatis核心配置文件中开启了延迟加载。
fetchType:"Lazy|eager":lazy表示延迟加载,eager表示立即加载
<!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where did=#{did}
</select>
@Test
public void testGetEmpAndDeptByStep(){
SqlSession session = SqlSessionUtils.getSession();
EmpMapper mapper = session.getMapper(EmpMapper.class);
Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
//System.out.println(empAndDeptByStepOne);
System.out.println(empAndDeptByStepOne.getSex());
System.out.println(empAndDeptByStepOne.getDept());
}
输出结果
DEBUG 06-25 21:37:58,197 ==> Preparing: select * from t_emp where eid=? (BaseJdbcLogger.java:137)
DEBUG 06-25 21:37:58,215 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 21:37:58,343 <== Total: 1 (BaseJdbcLogger.java:137)
男
DEBUG 06-25 21:37:58,343 ==> Preparing: select * from t_dept where did=? (BaseJdbcLogger.java:137)
DEBUG 06-25 21:37:58,343 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 21:37:58,345 <== Total: 1 (BaseJdbcLogger.java:137)
Dept{did=1, deptName='A'}
使用延迟加载我们会发现,我们只有用到了部门信息才会去执行查询部分信息的分布SQL语句,否则只会单单执行查询员工信息的基本语句。
3.一对多映射
public class Dept {
private Integer did;
private String deptName;
private List<Emp> emps;
}
a>collection
collection是集合标签
通过设置resultMap中的collection标签来,查询出的所有Emp类型放到list<Emp>集合中
collection:处理一对多的映射关系
ofType:表示该属性所对应的集合中存储数据的类型
/**
* 获取部门以及部门中所有的员工信息
*/
Dept getDeptAndEmp(@Param("did") Integer did);
<!--Dept getDeptAndEmp(@Param("did") Integer did);-->
<resultMap id="getDeptAndEmpResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<!--
collection:处理一对多的映射关系
ofType:表示该属性所对应的集合中存储数据的类型
-->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</collection>
</resultMap>
<select id="getDeptAndEmp" resultMap="getDeptAndEmpResultMap">
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did=#{did}
</select>
@Test
public void testGetDeptAndEmp(){
SqlSession session = SqlSessionUtils.getSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept deptAndEmp = mapper.getDeptAndEmp(1);
System.out.println(deptAndEmp);
}
结果:
DEBUG 06-25 22:47:51,342 ==> Preparing: select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did=? (BaseJdbcLogger.java:137)
DEBUG 06-25 22:47:51,361 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 22:47:51,372 <== Total: 3 (BaseJdbcLogger.java:137)
Dept{did=1, deptName='A', emps=[Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}, Emp{eid=4, empName='男', age=45, sex='男', email='123456', dept=null}, Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', dept=null}]}
b>分布查询
原理同多对一映射,将多对一映射中的association标签换为collection标签
/**
* 通过分布查询查询部门以及部门中所有的员工信息
* 分布查询第一步查询部门信息
*/
Dept getDeptAndEmpByStepOne(@Param("did") Integer did);
一对多的映射的分布查询也可以延迟加载,因为已经在核心配置文件中设置过自动开启延迟加载,所以这里用fetchType关闭这个查询延迟加载
<!--Dept getDeptAndEmpByStepOne(@Param("did") Integer did);-->
<resultMap id="getDeptAndEmpByStepOneResultMap" type="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
<collection property="emps"
select="com.cn.cslg.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="did"
fetchType="eager"
></collection>
</resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOneResultMap">
select * from t_dept where did = #{did};
</select>
这是分布查询的第二步 ,返回值设置为List<Emp>和Emp结果都一样,应该是底层自动帮我们更改了
/**
* 通过分布查询查询部门以及部门中所有的员工信息
* 分布查询第二步:根据did查询员工信息
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);
//Emp getDeptAndEmpByStepTwo(@Param("did") Integer did);
<!--List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);-->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where did = #{did}
</select>
这是关闭延迟加载输出结果
@Test
public void testGetDeptAndEmpByStep(){
SqlSession session = SqlSessionUtils.getSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
System.out.println(deptAndEmpByStepOne.getDeptName());
}
结果
DEBUG 06-25 22:54:05,848 ==> Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:05,865 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:05,879 ====> Preparing: select * from t_emp where did = ? (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:05,879 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:05,881 <==== Total: 3 (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:05,881 <== Total: 1 (BaseJdbcLogger.java:137)
A
这是开启延迟加载输出结果,更给fetchType为lazy即可
@Test
public void testGetDeptAndEmpByStep(){
SqlSession session = SqlSessionUtils.getSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1);
System.out.println(deptAndEmpByStepOne.getDeptName());
}
结果
DEBUG 06-25 22:54:59,172 ==> Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:59,191 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-25 22:54:59,243 <== Total: 1 (BaseJdbcLogger.java:137)
A
九.动态SQL
MyBatis框架的动态SQL技术是一种根据特定条件动态拼装的SQL语句功能,它存在的意义是为了解决凭借SQL语句字符串痛点的问题
1.if标签
- if标签可以通过test属性的表达式进行判断,若表达式结果为true,则标签中的内容会执行拼接;反之标签中的内容不会执行
- 需要在where后面添加一个恒成立条件例如:1=1,同时在第一个if语句前面也加上and
- 添加恒成立的条件不会影响sql语句
- 目的是为了防止第一个if判断失败,后面的语句直接出现and的情况
<select id="getEmpByConditionOne" resultType="Emp"> select <include refid="empColumns"></include> from t_emp where 1=1 <if test="empName != null and empName !='' " > and emp_name=#{empName} </if> <if test=" age != null and age !='' " > and age=#{age} </if> <if test="sex != null and sex !='' " > and sex=#{sex} </if> <if test="email != null and email !='' " > and email=#{email} </if> </select>
- 情况如下,为了避免出现select <include refid="empColumns"></include> from t_emp where的情况,所以需要添加一个恒成立条件
<select id="getEmpByConditionOne" resultType="Emp"> select <include refid="empColumns"></include> from t_emp where <if test="empName != null and empName !='' " > emp_name=#{empName} </if> <if test=" age != null and age !='' " > and age=#{age} </if> <if test="sex != null and sex !='' " > and sex=#{sex} </if> <if test="email != null and email !='' " > and email=#{email} </if> </select>
2.where标签
where标签:
- 当where标签中有内容时,会自动添加where关键字,并且将内容前的多余的And或Or去掉
- 当where标签中没有内容时,此时where标签没有任何效果
<select id="getEmpByConditionTwo" resultType="Emp"> select * from t_emp <where> <if test="empName != null and empName !='' " > and emp_name=#{empName} </if> <if test=" age != null and age !='' " > and age=#{age} </if> <if test="sex != null and sex !='' " > and sex=#{sex} </if> <if test="email != null and email !='' " > and email=#{email} </if> </where>
- 注意:where标签不能将内容后面的And或Or去掉,只能把内容前的And或Or去掉,如下:这种情况下,如果条件都成立,最后一个email判断语句或多出一个And,此时idea会报Sql语言错误
<select id="getEmpByConditionTwo" resultType="Emp"> select * from t_emp <where> <if test="empName != null and empName !='' " > emp_name=#{empName} and </if> <if test=" age != null and age !='' " > age=#{age} and </if> <if test="sex != null and sex !='' " > sex=#{sex} and </if> <if test="email != null and email !='' " > email=#{email} and </if> </where> </select>
3.trim标签
trim标签:
- 若标签中有内容
- prefix|suffix:将trim标签中内容的前面或后面添加指定内容,如and|or
- prefixOerrides|suffixOverrides:将trim标签中的内容前面或者后面去掉指定内容
- 若标签中没有内容
- trim标签页没有任何效果
<!--List<Emp> getEmpByConditionThree(Emp emp);--> <select id="getEmpByConditionThree" resultType="Emp"> select * from t_emp <trim prefix="where" suffixOverrides="and|or" > <if test="empName != null and empName !='' " > emp_name=#{empName} and </if> <if test=" age != null and age !='' " > age=#{age} or </if> <if test="sex != null and sex !='' " > sex=#{sex} and </if> <if test="email != null and email !='' " > email=#{email} and </if> </trim> </select>
@Test public void testDynamicSqlMapperThree(){ SqlSession session = SqlSessionUtils.getSession(); DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); //List<Emp> empByCondition = mapper.getEmpByConditionThree(new Emp(null, "张凯" ,23, "男", "123456")); //System.out.println(empByCondition); System.out.println(mapper.getEmpByConditionThree(new Emp(null,"张凯",23,"",""))); } 结果 DEBUG 06-27 22:54:53,308 ==> Preparing: select * from t_emp where emp_name=? and age=? (BaseJdbcLogger.java:137) DEBUG 06-27 22:54:53,325 ==> Parameters: 张凯(String), 23(Integer) (BaseJdbcLogger.java:137) DEBUG 06-27 22:54:53,342 <== Total: 0 (BaseJdbcLogger.java:137) []
age后面的and会被自动删除
4.choose,when,otherwise标签
相当于if | else..if | else
when标签至少有一个,otherwise标签最多有一个
<!--List<Emp> getEmpByChoose(Emp emp);--> <select id="getEmpByChoose" resultType="Emp"> select * from t_emp <where> <choose> <when test="empName != null and empName!='' "> emp_name=#{empName} </when> <when test=" age != null and age !='' " > age=#{age} </when> <when test="sex != null and sex !='' " > sex=#{sex} </when> <when test="email != null and email !='' " > email=#{email} </when> <otherwise> eid=1 </otherwise> </choose> </where> </select>
5.for-each标签
属性:
collections:设置需要循环的数组或者集合
item:表示数组或集合中的每一个数据
separator:表示玄幻体之间的间隔符号
open:foreach标签循环的所有内容的开始符号,在循环删除数据中in()会用到
close:foreach标签循环的所有内容的结束符号,在循环删除数据中in()会用到
Mapper映射获取传递值分类:
- 我们把mappe映射文件中获取传递值最终分为三种
- 实体类:直接通过属性名访问属性
- map集合:通过key访问
- 其他·:直接通过@param设置的key访问
- 批量删除
/**
* 通过数组实现批量删除
*/
int deleteMoreByArray(@Param("eids") Integer[] eids);
三种方法任选其一,一二两种是用in()删除数据,唯一的区别就是将前后()放在了open和close标签中,第三种是用or判断eid是否相等来删除
<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
(
<foreach collection="eids" item="eid" separator=",">
#{eid}
</foreach>
)
</delete>
<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
(
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
)
</delete>
<!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>
/**
* foreach实现批量删除,第一种xml方法
*/
@Test
public void testdeleteMoreByArray(){
SqlSession session = SqlSessionUtils.getSession();
DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
System.out.println(mapper.deleteMoreByArray(new Integer[]{6,7,8}));
}
结果
DEBUG 06-27 23:06:52,561 ==> Preparing: delete from t_emp where eid in ( ? , ? , ? ) (BaseJdbcLogger.java:137)
DEBUG 06-27 23:06:52,585 ==> Parameters: 6(Integer), 7(Integer), 8(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-27 23:06:52,586 <== Updates: 0 (BaseJdbcLogger.java:137)
0
- 批量添加
/**
* 通过list集合实现批量添加
*/
int insertMoreByList(@Param("emps") List<Emp> emps);
<!--int insertMoreByList(@Param("emps") List<Emp> emps);-->
<insert id="insertMoreByList">
insert into t_emp values
<foreach collection="emps" separator="," item="emp">
(null ,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null )
</foreach>
</insert>
/**
* foreach实现批量添加
*/
@Test
public void testinsertMoreByList(){
SqlSession session = SqlSessionUtils.getSession();
DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class);
Emp emp = new Emp(null,"z",23,"男","1123");
Emp emp1 = new Emp(null,"z",23,"男","1123");
Emp emp2 = new Emp(null,"z",23,"男","1123");
Emp emp3 = new Emp(null,"z",23,"男","1123");
List<Emp> list = new ArrayList<>();
list = Arrays.asList(emp,emp1,emp2,emp3);
int i = mapper.insertMoreByList(list);
System.out.println(i);
}
结果
DEBUG 06-27 23:17:28,149 ==> Preparing: insert into t_emp values (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) , (null ,?,?,?,?,null ) (BaseJdbcLogger.java:137)
DEBUG 06-27 23:17:28,168 ==> Parameters: z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String), z(String), 23(Integer), 男(String), 1123(String) (BaseJdbcLogger.java:137)
DEBUG 06-27 23:17:28,203 <== Updates: 4 (BaseJdbcLogger.java:137)
4
6.SQL片段
设置sql片段
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
应用sql片段
<include refid="empColumns"></include>
<sql id="empColumns">eid,emp_name,age,sex,email</sql>
<!--List<Emp> getEmpByConditionOne(Emp emp);-->
<select id="getEmpByConditionOne" resultType="Emp">
select <include refid="empColumns"></include> from t_emp where 1=1
<if test="empName != null and empName !='' " >
and emp_name=#{empName}
</if>
<if test=" age != null and age !='' " >
and age=#{age}
</if>
<if test="sex != null and sex !='' " >
and sex=#{sex}
</if>
<if test="email != null and email !='' " >
and email=#{email}
</if>
</select>
十.MyBatis的缓存
1.MyBatis的一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下一次查询相同的数据,就会直接到缓存直接获取,不会从数据库重新访问
使一级缓存失效的四种结果
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession不同的查询条件
- 同一个SqlSession两次查询期间执行了一次增删改插入操作,缓存影响的查询速度而不是结果
- 同一个SqlSession两次查询期间手动清空了缓存
/**
* MyBatis的一级缓存是自动开启的,作用域比较小,是同一个SqlSession范围内
*/
@Test
public void testCache(){
SqlSession session = SqlSessionUtils.getSession();
CacheMapper mapper = session.getMapper(CacheMapper.class);
Emp empByEid = mapper.getEmpByEid(1);
System.out.println(empByEid);
System.out.println(mapper.getEmpByEid(1));
System.out.println("\n一级缓存范围为同一个sqlSession,不会再一次执行sql语句,会到缓存中直接查找");
CacheMapper mapper1 = session.getMapper(CacheMapper.class);
System.out.println(mapper1.getEmpByEid(1));
System.out.println("\n一级缓存范围为同一个sqlSession,如果新建一个sqlSession一级缓存将没有效果");
SqlSession session1 = SqlSessionUtils.getSession();
CacheMapper mapper2 = session1.getMapper(CacheMapper.class);
System.out.println(mapper2.getEmpByEid(1));
}
结果:
DEBUG 06-28 20:57:04,244 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 20:57:04,263 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 20:57:04,280 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
一级缓存范围为同一个sqlSession,不会再一次执行sql语句,回到缓存中直接查找
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
一级缓存范围为同一个sqlSession,如果新建一个sqlSession一级缓存将没有效果
DEBUG 06-28 20:57:04,331 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 20:57:04,331 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 20:57:04,333 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
Process finished with exit code 0
/**
* 同一个SqlSession两次查询期间执行了一次增删改插入操作,缓存影响的查询速度而不是结果
*/
@Test
public void testCacheTwo(){
SqlSession session = SqlSessionUtils.getSession();
CacheMapper mapper = session.getMapper(CacheMapper.class);
System.out.println(mapper.getEmpByEid(1));
mapper.insertEmp(new Emp(null,"zk",18,"z","zz",null));
System.out.println(mapper.getEmpByEid(1));
}
结果
DEBUG 06-28 21:27:38,118 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,141 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,158 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
DEBUG 06-28 21:27:38,160 ==> Preparing: insert into t_emp values (null ,?,?,?,?,null ) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,161 ==> Parameters: zk(String), 18(Integer), z(String), zz(String) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,209 <== Updates: 1 (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,210 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,210 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:27:38,211 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=nu
/**
* MyBatis的一级缓存是自动开启的,作用域比较小,是同一个SqlSession范围内
*/
@Test
public void testCache(){
SqlSession session1 = SqlSessionUtils.getSession();
CacheMapper mapper2 = session1.getMapper(CacheMapper.class);
System.out.println(mapper2.getEmpByEid(1));
System.out.println("\n清空缓存");
session1.clearCache();
System.out.println(mapper2.getEmpByEid(1));
}
结果
DEBUG 06-28 21:35:49,526 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 21:35:49,526 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:35:49,527 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
清空缓存
DEBUG 06-28 21:35:49,528 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 21:35:49,528 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 21:35:49,529 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
2.MyBatis的二级缓存
二级换成是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被换成;此后若再次执行相同的查询语句,结果就会从缓存中获取,不会从数据库重新访问。
二级缓存开启条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置。
- 在映射文件文件中设置标签<cache><cache/>
- 二级换成必须啊在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类必须实现序列化接口
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存失效
public class Emp implements Serializable
<cache></cache>
<!--Emp getEmpByEid(@Param("eid")Integer eid);-->
<select id="getEmpByEid" resultType="Emp">
select * from t_emp where eid = #{eid}
</select>
/**
* 二级缓存
*/
@Test
public void testTwoCache(){
InputStream resourceAsStream=null;
try {
resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
SqlSession sqlSession1 = build.openSession(true);
System.out.println(sqlSession.getMapper(CacheMapper.class).getEmpByEid(1));
sqlSession.close();
System.out.println(sqlSession1.getMapper(CacheMapper.class).getEmpByEid(1));
sqlSession1.close();
}
输出结果:
DEBUG 06-28 22:50:49,976 Cache Hit Ratio [com.cn.cslg.mybatis.mapper.CacheMapper]: 0.0 (LoggingCache.java:60)
DEBUG 06-28 22:50:50,172 ==> Preparing: select * from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-28 22:50:50,202 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-28 22:50:50,216 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
WARN 06-28 22:50:50,221 As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66 (SerialFilterChecker.java:46)
DEBUG 06-28 22:50:50,224 Cache Hit Ratio [com.cn.cslg.mybatis.mapper.CacheMapper]: 0.5 (LoggingCache.java:60)
Emp{eid=1, empName='张三', age=12, sex='男', email='123456', dept=null}
3.二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
- eviction属性:缓存回收策略
LRU(Least Recently Used)--最近最少使用方法:移除最长时间不被使用的对象
FIFO(First in First Out)--先进先出方法:按对象进入缓存的顺序来一处他们;
SOFT--软引用方法:移除基于垃圾回收器状态的和软引用规则的对象;
WEAK--弱引用:更积极地一处基于垃圾回收期状态和弱引用规则地对象;
默认地是LRU;
- fushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
- size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
- readOnly属性:只读,true|false
true:只读缓存:会给所有调用者返回缓存对象地相同实例。因此这些对象不能被修改。者提供了很重要地性能优势。
false:读写缓存;会返回缓存对象地拷贝(通过序列化)。这回慢一些,但是安全因此默认是false
4.Mybatis缓存查询的顺序
查询是从大到小,即二级缓存到一级缓存;存储是从小到大,SqlSession关闭后将写入二级缓存。
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
5.整合第三方缓存EHCache
a>添加依赖
<!-- Mybatis EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
b>各个jar包的功能
jar包名称 | 作用 |
mybatis-ehcache | MyBatis和EHCache的整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日子门面包 |
logback-classic | 支持SLF4J门面接口的一个具体实现 |
c>创建EHCache的配置文件ehcahe.xml
名字必须为ehcahe.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径,可以自己更改 -->
<diskStore path="D:\ehcache\mybatis"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
d>设置二级缓存的类型
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
e>加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4H的具体实现logback来打印日志。
创建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.cn.cslg.mybatis.mapper" level="DEBUG"/>
</configuration>
f>EHCache配置文件说明
属性名 | 是否必须 | 作用 |
maxElementsInMemory | 是 | 在内存中缓存的element的最大数目 |
maxElementOnDisk | 是 | 在硬盘上魂村的element的最大数目,若是0表示无穷大 |
eternal | 是 | 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为falsename还要根据time ToldIeSeconds、timeToLiveSeconds判断 |
overflowToDisk | 是 | 设定放内存缓存溢出的时候是否将过期的element缓存到磁盘上 |
timeToIdleSeconds | 否 | 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便回删除,默认值是0,也就是可闲置时间无限大 |
timeToLiveSeconds | 否 | 缓存element的有效生命期,默认是0,也就是element存货时间无穷大 |
diskSpoolBufferSizeMB | 否 | DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区 |
diskPersistent | 否 | 在VM重启的时候是否启用磁盘保存的EhCache中的数据,默认是false |
diskExpiryThreadIntervalSeconds | 否 | 磁盘缓存的清理线程运行间隔,默认是120秒。每个102s,相应的线程会进行一次EhCache中数据的清理工作 |
memoryStoreEvictionPolicy | 否 | 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU(最近最少使用方法),可选的有LFU(最不常使用方法)和FIFO(先进先出方法) |
十一.MyBatis的逆向工程
- 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
- 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
- Java实体类
- Mapper接口(生成的实体类名+Mapper)
- Mapper映射文件(生成的实体类名+Mapper)
1.创建逆向工程的步骤(MyBatis逆向工程简洁版)
1.添加依赖和插件
plugin中的mysql驱动需要和外面的dependency驱动版本一样
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Mybatis-Maven-Parent</artifactId>
<groupId>cn.cslg.zk.mybatis</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Mybatis-Maven-module-04</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<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>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
2.创建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>
<properties resource="jdbc.properties"></properties>
<typeAliases>
<package name=""/>
</typeAliases>
<!--设置连接数据库的环境-->
<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>
<package name=""/>
</mappers>
</configuration>
3.创建逆向工程的配置文件
文件名必须为:generatorConfig.xml
<?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.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.cn.cslg.mybatis.pojo" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.cn.cslg.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.cn.cslg.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
4.执行MBG插件的generate目标
2.QBC查询(MyBatis逆向工程奢华尊享版)
创建过程和上面的简洁版的步骤一模一样,唯一的区别需要,唯一的区别就是将generatorConfig.xml配置文件里面的context标签的targetRuntime属性从MyBatis3Simple改为MyBatis3
比起简洁版本接口中的方法更多更具体
数据查询
通过条件查询数据的时候,先调用createCriteria方法然后通过链式调用and方法,如果需要添加or条件,通过方法.or()然后再链式调用你需要的or方法
- selectByExample:按条件查询,需要传入一个逆向工程创建出来的Example对象或者null,如果为null则会查询所有数据
- empExample.createCriteria().xxx:创建条件对象,通过and方法添加条件
- empExample.or().xxx:将值钱的条件通过or拼接起来
ublic void testMBG() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询所有数据
List<Emp> list = mapper.selectByExample(null);
list.forEach(emp -> System.out.println(emp));
//根据条件查询数据
System.out.println("根据条件查询数据\n\n");
EmpExample empExample =new EmpExample();
empExample.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThan(20);
//通过or方法将and条件链接起来
empExample.or().andDidIsNotNull();
List<Emp> list1 = mapper.selectByExample(empExample);
list1.forEach(emp -> System.out.println(emp));
}
结果:
DEBUG 06-30 16:41:12,520 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp (BaseJdbcLogger.java:137)
DEBUG 06-30 16:41:12,537 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 06-30 16:41:12,553 <== Total: 14 (BaseJdbcLogger.java:137)
查询所有数据
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
Emp{eid=2, empName='李四', age=4, sex='男', email='123456', did=2}
Emp{eid=3, empName='王五', age=456, sex='男', email='123456', did=3}
Emp{eid=4, empName='男', age=45, sex='男', email='123456', did=1}
Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}
Emp{eid=11, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=12, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=13, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=14, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=15, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=16, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=17, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=18, empName='z', age=23, sex='男', email='1123', did=null}
Emp{eid=19, empName='zk', age=18, sex='z', email='zz', did=null}
根据条件查询数据
DEBUG 06-30 16:41:12,562 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp WHERE ( emp_name = ? and age > ? ) or( did is not null ) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:41:12,563 ==> Parameters: 张三(String), 20(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:41:12,565 <== Total: 5 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
Emp{eid=2, empName='李四', age=4, sex='男', email='123456', did=2}
Emp{eid=3, empName='王五', age=456, sex='男', email='123456', did=3}
Emp{eid=4, empName='男', age=45, sex='男', email='123456', did=1}
Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}
数据修改
- updateByPrimaryKey:通过主键进行直接修改,如果传入属性为null,数据库中的数据也会被设置为null
- updateByprimaryKeySelective:通过主键进行选择性修改,如果字段为null则保持原样不修改
@Test
public void testMBG() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
System.out.println("\n\n直接修改前");
System.out.println(mapper.selectByPrimaryKey(1));
//update直接修改
mapper.updateByPrimaryKey(new Emp(1,"admin",12,null,"123",1));
System.out.println("\n\n直接修改后");
System.out.println(mapper.selectByPrimaryKey(1));
System.out.println("\n\n选择性修改前");
System.out.println(mapper.selectByPrimaryKey(2));
//update选择性修改,字段若为null则保持原样不修改
mapper.updateByPrimaryKeySelective(new Emp(2,"admin",12,null,"123",1));
System.out.println("\n\n选择性修改后");
System.out.println(mapper.selectByPrimaryKey(2));
}
结果:
直接修改前
DEBUG 06-30 16:58:35,714 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,734 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,747 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
DEBUG 06-30 16:58:35,748 ==> Preparing: update t_emp set emp_name = ?, age = ?, sex = ?, email = ?, did = ? where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,748 ==> Parameters: admin(String), 12(Integer), null, 123(String), 1(Integer), 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,749 <== Updates: 1 (BaseJdbcLogger.java:137)
直接修改后
DEBUG 06-30 16:58:35,749 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,750 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,751 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=1, empName='admin', age=12, sex='null', email='123', did=1}
选择性修改前
DEBUG 06-30 16:58:35,751 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,751 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,752 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=2, empName='admin', age=12, sex='男', email='123', did=1}
DEBUG 06-30 16:58:35,777 ==> Preparing: update t_emp SET emp_name = ?, age = ?, email = ?, did = ? where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,778 ==> Parameters: admin(String), 12(Integer), 123(String), 1(Integer), 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,779 <== Updates: 1 (BaseJdbcLogger.java:137)
选择性修改后
DEBUG 06-30 16:58:35,779 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp where eid = ? (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,780 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 16:58:35,781 <== Total: 1 (BaseJdbcLogger.java:137)
Emp{eid=2, empName='admin', age=12, sex='男', email='123', did=1}
Process finished with exit code 0
十二.分页插件
1.分页插件使用步骤
a>添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
b>配置分页插件
在MyBatis的核心配置文件中配置插件
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
2.分页插件的使用
使用MyBatis的分页插件功能实现分页功能
1.需要在查询功能之前开启分页
PageHelper.startPage(int pageNum,int pageSize)
2.在查询功能之后获取分页相关信息
PageInfo<Emp> pageInfo = new PageInfo<>(list,5)
list表示当前数据
5表示当前导航分页的数量
方法一:直接输出pageHelper.startPage的值
@Test
public void testPageHepler() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Page<Object> objects = PageHelper.startPage(2, 4);
//PageHelper.startPage(5, 4);
List<Emp> list = mapper.selectByExample(null);
//navigagatePages 导航页码展示数
//PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
//list.forEach(emp -> System.out.println(emp));
System.out.println(objects);
//System.out.println(pageInfo.getNavigatepageNums());
}
结果
DEBUG 06-30 20:07:40,792 Cache Hit Ratio [SQL_CACHE]: 0.0 (LoggingCache.java:60)
DEBUG 06-30 20:07:40,830 ==> Preparing: SELECT count(0) FROM t_emp (BaseJdbcLogger.java:137)
DEBUG 06-30 20:07:40,845 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 06-30 20:07:40,855 <== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 06-30 20:07:40,857 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp LIMIT ?, ? (BaseJdbcLogger.java:137)
DEBUG 06-30 20:07:40,857 ==> Parameters: 4(Long), 4(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 20:07:40,859 <== Total: 4 (BaseJdbcLogger.java:137)
Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=44, pages=11, reasonable=false, pageSizeZero=false}[Emp{eid=5, empName='钱七', age=456, sex='男', email='123456', did=1}, Emp{eid=11, empName='z', age=23, sex='男', email='1123', did=null}, Emp{eid=12, empName='z', age=23, sex='男', email='1123', did=null}, Emp{eid=13, empName='z', age=23, sex='男', email='1123', did=null}]
方法二:使用pageinfo获取数据集合
当查询获取到list集合的时候,使用PageInfo<Emp> pageInfo = new PageInfo<>(list,5)获取分页相关数据和一些自带的数据;
- PageInfo<Emp> pageInfo = new PageInfo<>(list,5)
- list表示当前数据
- 5表示当前导航分页的数量,最好为奇数
@Test
public void testPageHepler() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//Page<Object> objects = PageHelper.startPage(2, 4);
PageHelper.startPage(5, 4);
List<Emp> list = mapper.selectByExample(null);
//navigagatePages 导航页码展示数
PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
//list.forEach(emp -> System.out.println(emp));
//System.out.println(objects);
System.out.println(pageInfo);
}
结果
DEBUG 06-30 20:10:15,469 Cache Hit Ratio [SQL_CACHE]: 0.0 (LoggingCache.java:60)
DEBUG 06-30 20:10:15,508 ==> Preparing: SELECT count(0) FROM t_emp (BaseJdbcLogger.java:137)
DEBUG 06-30 20:10:15,522 ==> Parameters: (BaseJdbcLogger.java:137)
DEBUG 06-30 20:10:15,531 <== Total: 1 (BaseJdbcLogger.java:137)
DEBUG 06-30 20:10:15,533 ==> Preparing: select eid, emp_name, age, sex, email, did from t_emp LIMIT ?, ? (BaseJdbcLogger.java:137)
DEBUG 06-30 20:10:15,533 ==> Parameters: 16(Long), 4(Integer) (BaseJdbcLogger.java:137)
DEBUG 06-30 20:10:15,535 <== Total: 4 (BaseJdbcLogger.java:137)
list中打印的都是你分页所查询到的数据 ,等同于上面的直接输出pageHelper
PageInfo{pageNum=5, pageSize=4, size=4, startRow=17, endRow=20, total=44, pages=11,
list=Page{count=true, pageNum=5, pageSize=4, startRow=16, endRow=20, total=44, pages=11, reasonable=false, pageSizeZero=false}[Emp{eid=22, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=23, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=24, empName='null', age=null, sex='null', email='null', did=null}, Emp{eid=25, empName='null', age=null, sex='null', email='null', did=null}],
prePage=4, nextPage=6, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=3, navigateLastPage=7, navigatepageNums=[3, 4, 5, 6, 7]}
常用数据:
pageNum:当前页的页码
pageSize:每页显示的数据
size:当前页显示的真是条数(可能条数不够一页)
total:总记录数
pages:总分页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/是否为最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]