mybatis
官网
https://mybatis.org/mybatis-3/zh/index.html
简介
mybatis
MyBatis 本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到了Google Code,且改名为MyBatis 。2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
MyBatis是一个半自动ORM框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码。
ORM
ORM,Object-Relationl Mapping,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射,这样我们在具体的操作数据库的时候,只要像平时操作对象一样操作它就可以了,ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。
JDBC的缺点:需要手动的完成面向对象的Java语言、面向关系的数据库之间数据的转换,代码繁琐无技术含量,影响了开发效率。
查询是需要手动的将结果集的列数据转换为Java对象的属性;而添加操作时需要手动将Java对象的属性转换为数据库表的列字段。
关于面向对象的Java语言、面向关系的数据库之间数据的转换必须要做,问题在于这个转换是否可以不由开发者来做。可以的。ORM框架就是专门来做这个问题的,相当于在面向对象语言和关系数据库之间搭建一个桥梁。
项目搭建
引入依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--打印sql语句日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!--mybatis自动生成代码插件,用完必须注释掉-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<!--mybatis二级缓存 依赖 下面需要在配置文件中开启二级缓存-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
resource中mybatis-config.xml
<?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>-->
<!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!--<property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis"/>-->
<!--<property name="username" value="root"/>-->
<!--<property name="password" value="xqm@123456"/>-->
<!--</properties>-->
<!--全局参数设置 方案二-->
<!--如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
首先读取在 properties 元素体内指定的属性。
然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。-->
<properties resource="jdbc.properties">
<property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis1"></property>
<property name="url1" value="jdbc:mysql://124.223.81.248:3307/mybatis"></property>
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
<settings>
<!--开启二级缓存-->
<!--<setting name="cacheEnabled" value="true"/>-->
<!--开启懒加载/延迟加载 针对级联、关联查询使用-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!--将积极加载改为消极加载,即按需加载-->
<setting name="aggressiveLazyLoading" value="false"></setting>
<!--是否开启二级缓存 作用域是mapper/namespace 下面去各自的sql文件中单独开启二级缓存-->
<setting name="cacheEnabled" value="true"></setting>
</settings>
<typeAliases>
<!--<typeAlias type="com.xqm.model.User" alias="User"></typeAlias>-->
<!--直接给包设置别名-->
<package name="com.xqm.model"></package>
</typeAliases>
<!--设置连接数据库的环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--引用上面的全局变量-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="prduction">
<!--事务管理 MANAGED JDBC -->
<transactionManager type="JDBC"/>
<!--数据源配置 type="[UNPOOLED 不使用连接池|POOLED 连接池|JNDI]")-->
<dataSource type="POOLED">
<!--引用上面的全局变量-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url1}"/>
<property name="username" value="${username}"/>
<!--后面的:xqm@123456代表设置默认值-->
<property name="password" value="${password:xqm@123456}"/>
<!--默认是10 在任意时间可存在的活动(正在使用)连接数量-->
<property name="poolMaximumActiveConnections" value="10"></property>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/User1Mapper.xml"/>
<mapper resource="mappers/EmpMapper.xml"/>
<mapper resource="mappers/Emp1Mapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
<mapper resource="mappers/Dept1Mapper.xml"/>
<mapper resource="mappers/EmpDeptMapper.xml"/>
<mapper resource="mappers/UsersMapper.xml"/>
<mapper resource="mappers/OrdersMapper.xml"/>
<mapper resource="mappers/OrderdetailMapper.xml"/>
<mapper resource="mappers/ItemsMapper.xml"/>
<!--<package name="mappers"></package>-->
<!--使用package的时候必须要同名,并且必须在同一包下-->
</mappers>
</configuration>
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://124.223.81.248:3307/mybatis
username=root
password=自己的密码
UserMapper.xml
映射配置文件
resources下新建mappers目录,里面存放xxxxMapper.xml文件
resources.mappers.UserMapper.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">
<!--
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
-->
<mapper namespace="com.xqm.mapper.UserMapper">
<!--int insertUser();-->
<insert id="insertUser" parameterType="user">
insert into user values(null,#{username},#{age},'男')
</insert>
<!--#{}是预编译处理 ${}是字符串替换-->
<select id="selectAll" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<insert id="insertNewUser" parameterType="user">
insert into user values(#{id},#{username},#{age},#{sex})
</insert>
<update id="updateTest" parameterType="user">
update user set age = #{age} where id=#{id}
</update>
<delete id="deleteTest" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
user.sql
/*
Navicat Premium Data Transfer
Source Server : 124.223.81.248-3307-自己数据库
Source Server Type : MySQL
Source Server Version : 80027
Source Host : 124.223.81.248:3307
Source Schema : mybatis
Target Server Type : MySQL
Target Server Version : 80027
File Encoding : 65001
Date: 22/08/2022 15:22:54
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`username` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`age` int(0) NULL DEFAULT NULL,
`sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
model.User.class
实体类
package com.xqm.model;
/**
* @author xqm
* @Class: TODO
* @Description:
* @date: 2022年05月30日 14:19
*/
public class User {
private Integer id;
private String username;
private Integer age;
private String sex;
public User() {
}
public User(Integer id, String username, Integer age, String sex) {
this.id = id;
this.username = username;
this.age = age;
this.sex = sex;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
mapper.UserMapper.interface
映射接口
package com.xqm.mapper;
import com.xqm.model.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
/**
*
* 1.mapper接口映射文件的namespace要和mapper接口的全类名保持一致
* 2.方法名要和配置文件中的id一致
* @return
*/
int insertUser(@Param("username") String username,@Param("age") int age);
List<User> selectAll(int id);
void insertNewUser(User user);
void updateTest(@Param("age") int age,@Param("id") int id);
void deleteTest(int id);
}
mapper映射文件和核心配置文件关系
log4j日志
resource下新建log4j.properties文件
# \u5168\u5C40\u65E5\u5FD7\u914D\u7F6E
log4j.rootLogger=ERROR, stdout
# MyBatis \u65E5\u5FD7\u914D\u7F6E
#log4j.logger.com.xqm=TRACE
# \u63A7\u5236\u53F0\u8F93\u51FA
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
测试
package test;
import com.xqm.mapper.EmpDeptMapper;
import com.xqm.mapper.User1Mapper;
import com.xqm.mapper.UserMapper;
import com.xqm.model.EmpDept;
import com.xqm.model.User;
import com.xqm.model.User1;
import com.xqm.util.AutoNameUtil;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* @author xqm
* @Class: TODO
* @Description:
* @date: 2022年05月30日 14:37
*/
public class TestMybatis {
private SqlSessionFactory factory;
@Test
public void testMybatis() throws IOException {
// 获取SqlSession
SqlSession sqlSession = factory.openSession();
//通过代理模式创建UserMapper接口的代理实现类对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
// int result = userMapper.insertUser();
// sqlSession.commit();
List<User> users = userMapper.selectAll(7);
//提交事务
// sqlSession.commit();
// System.out.println("result:" + result);
// for (User user : users) {
// System.out.println(user.toString());
// }
sqlSession.close();
}
@Before
public void before() throws IOException {
// 加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory
factory = ssfb.build(resourceAsStream, "prduction");
}
}
mybatis-config.xml
properties
全局参数设置,需要使用时用${name}来使用
properties的优先级最低
参数的优先级:
方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性
比如下面,就会优先使用jdbc.properties文件中的数据库配置文件
<properties resource="jdbc.properties">
<property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis1"></property>
<property name="url1" value="jdbc:mysql://124.223.81.248:3307/mybatis"></property>
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
settings
设置自带属性的开闭
<settings>
<!--开启二级缓存-->
<!--<setting name="cacheEnabled" value="true"/>-->
<!--开启懒加载/延迟加载 针对级联、关联查询使用-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!--将积极加载改为消极加载,即按需加载-->
<setting name="aggressiveLazyLoading" value="false"></setting>
<!--是否开启二级缓存 作用域是mapper/namespace 下面去各自的sql文件中单独开启二级缓存-->
<setting name="cacheEnabled" value="true"></setting>
</settings>
typeAliases
设置实体类的别名,mappers.xxxMapper.xml中,可以通过别名来查找到实体类
resultType 返回值类型 和paramterType 上就可以使用别名了
可以直接设置一个类的别名,或者设置整个包,这也这个包下所有的类的都可以使用别名
<typeAliases>
<!--<typeAlias type="com.xqm.model.User" alias="User"></typeAlias>-->
<!--直接给包设置别名-->
<package name="com.xqm.model"></package>
</typeAliases>
environments
数据库环境设置,可以设置多个,比如生产环境、测试环境、开发环境
<!--设置连接数据库的环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--引用上面的全局变量-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="prduction">
<!--事务管理 MANAGED JDBC -->
<transactionManager type="JDBC"/>
<!--数据源配置 type="[UNPOOLED 不使用连接池|POOLED 连接池|JNDI]")-->
<dataSource type="POOLED">
<!--引用上面的全局变量-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url1}"/>
<property name="username" value="${username}"/>
<!--后面的:xqm@123456代表设置默认值-->
<property name="password" value="${password:xqm@123456}"/>
<!--默认是10 在任意时间可存在的活动(正在使用)连接数量-->
<property name="poolMaximumActiveConnections" value="10"></property>
</dataSource>
</environment>
</environments>
mappers
引入映射文件,这样mybatis就可以找到mapper.xml文件,通过mapper.xml文件的namespace找到映射的mapper接口,通过参数类型parameterType找到实体类
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/UsersMapper.xml"/>
<mapper resource="mappers/OrdersMapper.xml"/>
<mapper resource="mappers/OrderdetailMapper.xml"/>
<mapper resource="mappers/ItemsMapper.xml"/>
<!--<package name="mappers"></package>-->
<!--使用package的时候必须要同名,并且必须在同一包下-->
</mappers>
一共有四种方式:
resource、url、class、package
mybatis的普通模式开发
概念
普通模式,也称为传统DAO模式,就是在传统DAO模式下,定义接口和实现类,如 interface EmpDao
class EmpDaoImpl implements EmpDao. 在实现类中,用SQLSession对象调用 select insert delete update 等方法实现.目前极为少见.在传统模式下,我们需要知道SqlSession对象 实现CURD和 参数传递的处理
普通模式开发现在基本不用
sqlSession查询的三种方式
SqlSession对象本身的API中就有三个查询方法,分别能够实现如下查询方式
1返回单个对象 selectOne
2返回对象List集合 selectList
3返回对象Map集合 selectMap
实体类
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
}
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="EmpMapper">
<!--
返回单个对象
public Emp findOne();
id 相当于方法名
resultType 相当于返回值类型
sql语句的查询结果用哪个类来进行封装 如果返回值类型是集合,这里写的也是集合中的元素对应的类,不是集合本身作为类型
paramaterType 参数类型
SQL语句就是具体的方法体的实现
-->
<select id="findOne" resultType="emp" >
select * from emp where empno = 7499
</select>
<!--
返回多个对象List集合
查询全部的员工信息
public List<Emp> findAll()
-->
<select id="findAll" resultType="emp">
select * from emp
</select>
<!--返回多个对象的Map集合
把查询出来的数据中的某一列作为键,整条数据封装的对象作为值
public Map<key,Emp> findEmpMap()
<empno,Emp>
<key,Emp>
-->
<select id="findEmpMap" resultType="map">
select * from emp
</select>
</mapper>
mybatis-config导入配置文件
<mappers>
<mapper resource="com/msb/mapper/DeptMapper.xml"/>
<mapper resource="com/msb/mapper/EmpMapper.xml"/>
</mappers>
测试
package com.msb.test;
import com.msb.pojo.Dept;
import com.msb.pojo.Emp;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Author: Ma HaiYang
* @Description: MircoMessage:Mark_7001
*/
public class Test2 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testSelectOne(){
// 查询单个对象
System.out.println("sqlSession查询单个对象");
Emp emp = sqlSession.selectOne("findOne");
System.out.println(emp);
}
@Test
public void testSelectList(){
// 查询多个对象的List集合
System.out.println("sqlSession查询对象List集合");
List<Emp> emps = sqlSession.selectList("EmpMapper.findAll");
emps.forEach(System.out::println);
}
@Test
public void testSelectMap(){
// 查询多个对象的Map集合
System.out.println("sqlSession查询对象Map集合");
Map<Integer, Emp> empMap = sqlSession.selectMap("findEmpMap", "EMPNO");
Set<Integer> empnos = empMap.keySet();
for (Integer empno : empnos) {
System.out.println(empno+" :" +empMap.get(empno));
}
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}
mybatis参数传递的三种方式
1 单个基础数据类型作为参数
2 多个基础数据类型的map 集合作为参数
3 引用类型作为参数
mapper的映射文件
<?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="EmpMapper2">
<!--
参数为一个基本数据类型
根据员工工号查询员工的全部信息,返回单个员工对象
public Emp findByEmpno(int empno);
parameterType 在有参数情况下也是可以省略不写 mybatis 可以根据实际情况自动判断
如果要写parameterType 那么就要写对
在SQL语句上可以使用${} #{} 代表参数的占位
如果参数是单个基本数据类型,{}中名字可以随便写,见名知意
${} 代表mybatis底层使用Statment语句对象,参数是以字符串拼接的形式设置
#{} 代表mybatis底层使用的preparedStatment语句对象,参数使用?作为占位符处理
#{} 以后常用
-->
<select id="findByEmpno" resultType="emp" parameterType="int">
select * from emp where empno = #{empno}
</select>
<!--
参数为map集合
查询指定部门号和指定最低薪资的员工信息
20 号部门 且工资在1500以上的员工信息
public List<Emp> findEmpByDeptnoAndSal(int deptno,double sal);
< > 最好要进行转译处理,参照HTML转译 w3school在线文档中有转译符号对应规则
Map<String,Object> args=new HashMap<>();
args.put("deptno", 20);
args.put("sal", 1500.0);
#{}中写的是map集合中,参数的键
-->
<select id="findEmpByDeptnoAndSal" resultType="emp" parameterType="map">
select * from emp where deptno = #{deptno} and sal >= #{sal}
</select>
<!--
参数为对象
emp >>> deptno sal
参数是我们自定义的类型,那么 #{}中写的是参数的属性名
-->
<select id="findEmpByDeptnoAndSal2" resultType="emp" parameterType="emp">
select * from emp where deptno = #{deptno} and sal >= #{sal}
</select>
</mapper>
测试
package com.msb.test;
import com.msb.pojo.Emp;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Author: Ma HaiYang
* @Description: MircoMessage:Mark_7001
*/
public class Test3 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testSingleArg(){
// 测试单个基本数据类型作为参数
Emp emp = sqlSession.selectOne("findByEmpno", 7499);
System.out.println(emp);
}
@Test
public void testMapArg(){
// 测试Map集合作为参数
Map<String,Object> args=new HashMap<>();
args.put("deptno", 20);
args.put("sal", 3000.0);
List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal", args);
emps.forEach(System.out::println);
}
@Test
public void testEmpArg(){
// 测试Map集合作为参数
Emp arg =new Emp();
arg.setDeptno(10);
arg.setSal(2000.0);
List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal2", arg);
emps.forEach(System.out::println);
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}
mybatis代理模式开发
前面已经使用MyBatis完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的,实现了MyBatis的入门。
但是却存在如下缺点:
- 不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都只能提供一个查询参数。如果要多个参数,需要封装到JavaBean或者Map中,并不一定永远是一个好办法。
- 返回值类型较固定。
- 只提供了映射文件,没有提供数据库操作的接口,不利于后期的维护扩展。
在MyBatis中提供了另外一种成为 Mapper代理(或称为接口绑定) 的操作方式。在实际开发中也使用该方式。下面我们就是要Mapper代理的方式来实现对Emp表的CRUD操作吧,还有完成多个参数传递、模糊查询、自增主键回填等更多的技能实现。搭建好的项目框架如图所示,相比而言,增加了接口EmployeeMapper。但是却会引起映射文件和测试类的变化。
优点:
1有接口 模块之间有规范了
2参数的处理多样了,接口中的方法参数列表由我们自己决定
3通过代理模式由mybatis提供接口的实现类对象 我们不用写实现类了
mapper接口
package com.xqm.mapper;
import com.xqm.model.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
/**
*
* 1.mapper接口映射文件的namespace要和mapper接口的全类名保持一致
* 2.方法名要和配置文件中的id一致
* @return
*/
int insertUser(@Param("username") String username,@Param("age") int age);
List<User> selectAll(int id);
void insertNewUser(User user);
void updateTest(@Param("age") int age,@Param("id") int id);
void deleteTest(int id);
}
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">
<!--
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
-->
<mapper namespace="com.xqm.mapper.UserMapper">
<!--int insertUser();-->
<insert id="insertUser" parameterType="user">
insert into user values(null,#{username},#{age},'男')
</insert>
<!--#{}是预编译处理 ${}是字符串替换-->
<select id="selectAll" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<insert id="insertNewUser" parameterType="user">
insert into user values(#{id},#{username},#{age},#{sex})
</insert>
<update id="updateTest" parameterType="user">
update user set age = #{age} where id=#{id}
</update>
<delete id="deleteTest" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
测试代码
@Before
public void before() throws IOException {
// 加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
// 获取SqlSessionFactoryBuilder
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory
factory = ssfb.build(resourceAsStream, "prduction");
}
@Test
public void insertTest(){
SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int result = userMapper.insertUser(AutoNameUtil.autoSurAndName(),13);
sqlSession.commit();
sqlSession.close();
}
参数传递
有以下类型的参数传递:
1.单个基本数据类型
2.多个基本数据类型
3.单个引用数据类型
4.map集合数据类型
5.多个引用数据类型
接口
package com.msb.mapper;
import com.msb.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @Author: Ma HaiYang
* @Description: MircoMessage:Mark_7001
*/
public interface EmpMapper {
/**
* 该方法用于查询全部的员工信息
* @return 全部员工信息封装的Emp对象的List集合
*/
List<Emp> findAll();
/**
* 根据员工编号查询单个员工信息的方法
* @param empno 员工编号
* @return 如果找到了返回Emp对象,找不到返回null
*/
Emp findByEmpno(int empno);
/**
* 根据员工编号和薪资下限去查询员工信息
* @param empno 员工编号
* @param sal 薪资下限
* @return 多个Emp对象的List集合
*/
List<Emp> findByDeptnoAndSal(@Param("deptno") int deptno,@Param("sal") double sal);
List<Emp> findByDeptnoAndSal2(Map<String,Object> map);
List<Emp> findByDeptnoAndSal3(Emp emp);
List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
}
mapper映射文件
<?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.msb.mapper.EmpMapper">
<!--
1 接口的名字和Mapper映射为文件名字必须保持一致(不包含拓展名)
2 Mapper映射文件的namespace必须是接口的全路径名
3 sql语句的id必须是对应方法的名
4 DeptMapper映射文件应该和接口编译之后放在同一个目录下
-->
<!--List<Emp> findAll();-->
<select id="findAll" resultType="emp" >
select * from emp
</select>
<!--
单个基本数据类型作为方法参数
#{}中可以随便写,遵循见名知意
Emp findByEmpno(int empno);
-->
<select id="findByEmpno" resultType="emp" >
select * from emp where empno =#{empno}
</select>
<!--
多个基本数据类型作为方法参数
List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
方式1 arg* arg0 arg1 arg2 数字是索引,从0开始
方式2 param* param1 param2 param3 数字是编号,从1开始
使用别名
List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
通过@Param注解使用别名之后,就不能再使用arg* 但是可以继续使用param*
-->
<select id="findByDeptnoAndSal" resultType="emp">
<!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
<!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
<!-- select * from emp where deptno =#{deptno} and sal >= #{sal}-->
</select>
<!--
参数是map,{}写键的名字
-->
<select id="findByDeptnoAndSal2" resultType="emp" parameterType="map" >
<!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
<!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
select * from emp where deptno =#{deptno} and sal >= #{sal}
</select>
<!--单个引用类型,{}中写的使用对象的属性名-->
<select id="findByDeptnoAndSal3" resultType="emp" parameterType="emp" >
select * from emp where deptno =#{deptno} and sal >= #{sal}
</select>
<!--
多个引用类型作为方法参数
List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
-->
<select id="findByDeptnoAndSal4" resultType="emp" >
<!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
select * from emp where deptno =#{param1.deptno} and sal >= #{param2.sal}
<!-- select * from emp where deptno =#{empa.deptno} and sal >= #{empb.sal}-->
</select>
</mapper>
模糊查询
在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。
and username like concat('%',#{username,jdbcType=VARCHAR},'%')
小于的时候使用<![CDATA[<]]>
<if test="age!=null">
and age <![CDATA[<]]> #{age}
</if>
<!--List<Emp> getByName(String name);-->
<select id="findByEname" resultType="emp" >
select * from emp where ename like concat('%',#{name},'%')
</select>
动态SQL
MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。
if标签
示例:
<select id="findByCondition" resultType="emp">
select * from emp where 1=1
<if test="empno != null">
and empno =#{empno}
</if>
<if test="ename != null and ename != ''">
and ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and job =#{job}
</if>
</select>
where标签
where标签中的内容如果全部都不满足,那么where关键字也不会出现。如果满足,那么where后面的第一个and会自动删除
<select id="findEmpByCondition" resultType="emp">
select * from emp
<where>
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
<if test="job != null and job != ''">
and job= #{job}
</if>
<if test="mgr != null ">
and mgr= #{mgr}
</if>
<if test="hiredate != null ">
and hiredate= #{hiredate}
</if>
<if test="sal != null">
and sal= #{sal}
</if>
<if test="comm != null ">
and comm =#{comm}
</if>
<if test="deptno != null ">
and deptno= #{deptno}
</if>
</where>
</select>
choose标签和when标签
类似于java的switch,只会满足一个when。如果都不满足,就会使用otherwise标签
<select id="findEmpByCondition2" resultType="emp">
select * from emp
<where>
<choose>
<when test="empno != null">
and empno= #{empno}
</when>
<when test="ename != null and ename != ''">
and ename= #{ename}
</when>
<when test="job != null and job != ''">
and job= #{job}
</when>
<when test="mgr != null ">
and mgr= #{mgr}
</when>
<when test="hiredate != null ">
and hiredate= #{hiredate}
</when>
<when test="sal != null">
and sal= #{sal}
</when>
<when test="comm != null ">
and comm =#{comm}
</when>
<when test="deptno != null ">
and deptno= #{deptno}
</when>
<otherwise>
and owner = "admin"
</otherwise>
</choose>
</where>
</select>
set标签
特点:
set标签用于更新语句中
set标签解析为set关键字
set可以去除跟新语句中无用的逗号
通常是和if标签一起使用
<update id="updateEmpByCondtion" >
update emp
<set>
<if test="ename != null and ename != '' ">
, ename =#{ename}
</if>
<if test="job != null and ename != '' ">
, job =#{job}
</if>
<if test="mgr != null ">
, mgr =#{mgr}
</if>
<if test="hiredate != null ">
, hiredate =#{hiredate}
</if>
<if test="sal != null ">
, sal =#{sal}
</if>
<if test="comm != null ">
, comm =#{comm}
</if>
<if test="deptno != null ">
, deptno =#{deptno}
</if>
</set>
where empno =#{empno}
</update>
trim标签
处理前缀后缀
<update id="updateEmpByCondition2" >
update emp
<!--prefix 要增加什么前缀
prefixOverrides 要去除什么前缀
suffix 要增加什么后缀
suffixOverrides 要去除什么后缀
set 是trim的一种特殊情况
-->
<trim prefix="set" suffixOverrides="," >
<if test="ename != null and ename != ''">
ename= #{ename},
</if>
<if test="job != null and job != ''">
job= #{job},
</if>
<if test="mgr != null ">
mgr= #{mgr},
</if>
<if test="hiredate != null ">
hiredate= #{hiredate},
</if>
<if test="sal != null">
sal= #{sal},
</if>
<if test="comm != null ">
comm =#{comm},
</if>
<if test="deptno != null ">
deptno= #{deptno},
</if>
</trim>
where empno = #{empno}
</update>
示例二:
<select id="findEmpByCondition" resultType="emp">
select * from emp
<trim prefix="where" prefixOverrides="and">
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
</trim>
</select>
bind标签
一般用于模糊查询,key-value的方式
能够通过bind中的name使用bind中的value
<select id="selectByMap1" parameterType="java.util.Map" resultType="User1">
<bind name="concatName" value="'%'+username+'%'"></bind>
<include refid="selectAll"></include>
where
username like #{concatName} and
age in
<foreach collection="user" item="age" separator="," open=" (" close=")">
#{age}
</foreach>
</select>
sql标签和include标签
sql标签是自定义一段sql语句,通过include标签能够进行复用sql标签中的sql语句
<!--实现重复代码的重用-->
<sql id="selectAll">
select * from user1
</sql>
<select id="selectForTest1" parameterType="java.util.List" resultType="User1">
<include refid="selectAll"></include>
<where>
age in
<foreach collection="array" open=" (" close=")" separator="," item="age">
#{age}
</foreach>
</where>
</select>
foreach标签
通过foreach标签遍历传递进来的数组或集合
<!--List<Emp> findByEmpnos1(int[] empnos);
collection="" 遍历的集合或者是数组
参数是数组,collection中名字指定为array
参数是List集合,collection中名字指定为list
separator="" 多个元素取出的时候 用什么文字分隔
open="" 以什么开头
close="" 以什么结尾
item="" 中间变量名
for(Person per:PersonList)
-->
<select id="selectByMap" parameterType="java.util.Map" resultType="User1">
<include refid="selectAll"></include>
where age in
<foreach collection="user" item="age" separator="," open=" (" close=")">
#{age}
</foreach>
</select>
多表查询
一对一、一对多、多对多
一对一
实体类
package com.xqm.model;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String username;
private Integer age;
private String sex;
// private Integer deptNum;
// 将相关联的depyNum更改为关联的实体类
private Dept dept;
}
mapper.xml
association是处理一对一关系
<?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.xqm.mapper.EmpMapper">
<resultMap id="EmpAndDept" type="com.xqm.model.Emp">
<id column="id" property="id"></id>
<result property="username" column="username"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<association property="dept" javaType="dept">
<id column="id" property="id"></id>
<result property="deptName" column="dept_name"></result>
<result property="deptContemt" column="dept_contemt"></result>
</association>
</resultMap>
<select id="selectData" resultMap="EmpAndDept">
select e.*,d.* from emp e inner join dept d on e.dept_num=d.id
</select>
</mapper>
一对多
一对多就是在实体类中组合一个list。mapper.xml使用collection来处理一对多的关系
实体类
package com.xqm.model;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp1 {
private Integer id;
private String username;
private Integer age;
private String sex;
List<Dept1> dept1List;
}
接口
package com.xqm.mapper;
import com.xqm.model.Emp1;
import java.util.List;
public interface Emp1Mapper {
Emp1 selectAll(int id);
}
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.xqm.mapper.Emp1Mapper">
<resultMap id="EmpAndDept" type="com.xqm.model.Emp1">
<id column="id" property="id"></id>
<result property="username" column="username"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<collection property="dept1List" ofType="Dept1" javaType="list">
<id column="id" property="id"></id>
<result property="deptName" column="dept_name"></result>
<result property="deptContemt" column="dept_contemt"></result>
</collection>
</resultMap>
<select id="selectAll" resultMap="EmpAndDept" parameterType="int">
select e.*,d.* from emp1 e , dept1 d where e.id=d.emp_id and e.id=#{id}
</select>
</mapper>
多对多
多对多实际上就是一对多+一对一/一对多
实体类
package com.xqm.model;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
private Integer uid;
private String uname;
private String sex;
private Date birthday;
private String address;
// 用户和订单是1对多关系
private List<Orders> ordersList;
}
实体类2
package com.xqm.model;
import java.util.Date;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders {
private Integer oid;
private Integer userid;
private String orderid;
private Date createtime;
private String status;
// 订单和订单详情是一对多关系
private List<Orderdetail> orderdetailList;
}
接口
package com.xqm.mapper;
import com.xqm.model.Users;
import com.xqm.util.Page;
import org.apache.ibatis.session.RowBounds;
import java.util.List;
public interface UsersMapper {
// 根据用户编号,查询下面所有的订单、订单详情、商品信息
List<Users> selectAll(int uid);
int selectCount();
List<Users> selectUsersByPage(Page page);
// 分页插件
List<Users> selectUsersByRows(RowBounds rb);
}
映射配置文件
collection+association的组合
<?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.xqm.mapper.UsersMapper">
<resultMap id="getAll" type="com.xqm.model.Users">
<id column="uid" property="uid"></id>
<result column="uname" jdbcType="VARCHAR" property="uname"/>
<result column="sex" jdbcType="VARCHAR" property="sex"/>
<result column="birthday" jdbcType="DATE" property="birthday"/>
<result column="address" jdbcType="VARCHAR" property="address"/>
<collection property="ordersList" ofType="orders" javaType="list">
<id column="oid" property="oid"></id>
<result property="status" column="status"></result>
<result property="createtime" column="createTime"></result>
<result property="userid" column="userid"></result>
<result property="orderid" column="orderid"></result>
<collection property="orderdetailList" ofType="Orderdetail" javaType="list">
<id column="odid" property="odid"></id>
<!--<result property="itemid" column="itemid"></result>-->
<result property="orderid" column="orderid"></result>
<result property="itemnum" column="itemnum"></result>
<association property="items" javaType="items">
<id column="iid" property="iid"></id>
<result property="name" column="name"></result>
<result property="detail" column="detail"></result>
<result property="price" column="price"></result>
</association>
</collection>
</collection>
</resultMap>
<!--
select u.uname,u.sex,u.address,
o.orderid,o.status,
d.itemnum,
i.name,i.detail,i.price
from users u
inner join orders o on u.uid=o.userid
inner join orderdetail d on d.orderid=o.orderid
inner join items i on i.iid= d.itemid
WHERE u.uid=#{uid}
uid, uname, sex, birthday, address, oid, userid, orderid, createTime, status, odid, orderid, itemid, itemnum, iid, name, detail, price
-->
<select id="selectAll" parameterType="int" resultMap="getAll">
select u.*,o.*,d.*,i.*
from users u
inner join orders o on u.uid=o.userid
inner join orderdetail d on d.orderid=o.orderid
inner join items i on i.iid= d.itemid
WHERE u.uid=#{uid}
</select>
<select id="selectCount" resultType="int">
select count(uid) from users
</select>
<select id="selectUsersByPage" parameterType="com.xqm.util.Page" resultType="users">
<bind name="beginIndex" value="(currentPage-1)*pageCount"></bind>
select * from users order by uid limit #{beginIndex},#{pageCount}
</select>
<select id="selectUsersByRows" resultType="users">
select * from users
</select>
</mapper>
resultMap属性
property | 需要映射到JavaBean 的属性名称。 |
---|---|
javaType | property的类型,一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。 |
column | 数据表的列名或者列别名。 |
jdbcType | column在数据库表中的类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。 |
typeHandler | 使用这个属性可以覆写类型处理器,实现javaType、jdbcType之间的相互转换。一般可以省略,会探测到使用的什么类型的typeHandler进行处理 |
fetchType | 自动延迟加载 |
select | association、collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称 |
ofType | collection的属性,指明集合中元素的类型(即泛型类型) |
延迟加载/懒加载
延迟加载,又称按需加载。 延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。
延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快。
延迟加载的设置
第一步:全局开关:在mybatis-config.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
lazyLoadingEnabled :是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
aggressiveLazyLoading: 当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,
第二步:分开关:指定的association和collection元素中配置fetchType属性。eager:表示立刻加载;lazy:表示延迟加载。 将覆盖全局延迟设置 。
缓存
概念
MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。
一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
一级缓存
一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。
缓存中的数据使用键值对形式存储数据
namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值
中间发生了增删改或者是调用了SqlSession调用了**commit,* 会自动清空缓存
sqlSession.commit();// 增删改的时候调用
二级缓存
二级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口
接口
public interface EmpMapper {
Emp findByEmpno(int empno);
}
mapper
<mapper namespace="com.msb.mapper.EmpMapper">
<cache/>
<select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">
select * from emp where empno =#{empno}
</select>
</mapper>
测试代码
package com.msb.test;
import com.msb.mapper.EmpMapper;
import com.msb.pojo.Emp;
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.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author: Ma HaiYang
* @Description: MircoMessage:Mark_7001
*/
public class Test3 {
private SqlSession sqlSession;
private SqlSession sqlSession2;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
sqlSession2=factory.openSession();
}
@Test
public void testFindDeptByDetpno() {
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.findByEmpno(7521);
System.out.println(emp);
// SqlSession提交之后,才会将查询的结果放入二级缓存
sqlSession.commit();
EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
Emp emp2 = mapper2.findByEmpno(7521);
System.out.println(emp2);
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
sqlSession2.close();
}
}
注意其中的commit(),执行该命令后才会将该SqlSession的查询结果从一级缓存中放入二级缓存,供其他SqlSession使用。另外执行SqlSession的close()也会将该SqlSession的查询结果从一级缓存中放入二级缓存。两种方式区别在当前SqlSession是否关闭了。
执行结果显示进行了两次对数据库的SQL查询,说明二级缓存并没有开启。需要进行如下步骤完成开启。
1.全局开关:在mybatis-config.xml文件中的<settings>标签配置开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2.分开关:在要开启二级缓存的mapper文件中开启缓存
<mapper namespace="com.msb.mapper.EmployeeMapper">
<cache/>
</mapper>
3.二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口,
public class Emp implements Serializable { }
经过设置后,查询结果如图所示。发现第一个SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库, 在commit()或者close()的时候将数据放入到二级缓存 。第二个SqlSession执行相同SQL语句查询时就直接从二级缓存中获取了。
注意:
- MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口。
- 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
- 加入Cache元素后,会对相应命名空间所有的select元素查询结果进行缓存,而其中的insert、update、delete在操作是会清空整个namespace的缓存。
- cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking。
<cache type="" readOnly="" eviction=""flushInterval=""size=""blocking=""/>
属性 | 含义 | 默认值 |
---|---|---|
type | 自定义缓存类,要求实现org.apache.ibatis.cache.Cache接口 | null |
readOnly | 是否只读true:给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。false:会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全 | false |
eviction | 缓存策略LRU(默认) – 最近最少使用:移除最长时间不被使用的对象。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。 | LRU |
flushInterval | 刷新间隔,毫秒为单位。默认为null,也就是没有刷新间隔,只有执行update、insert、delete语句才会刷新 | null |
size | 缓存对象个数 | 1024 |
blocking | 是否使用阻塞性缓存BlockingCachetrue:在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,保证只有一个线程到数据库中查找指定key对应的数据false:不使用阻塞性缓存,性能更好 | false |
5.如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false。useCache控制当前sql语句是否启用缓存flushCache控制当前sql执行一次后是否刷新缓存
<select id =“findByEmpno” resultType =“emp” useCache =“true” flushCache =“false” > |
---|
逆向工程
generatorConfig.xml
<!DOCTYPE generatorConfiguration PUBLIC
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="simple" targetRuntime="MyBatis3">
<!-- 注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
<property name="suppressDate" value="false"/> <!-- 是否生成注释代时间戳-->
</commentGenerator>
<!--指向数据库连接-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://124.223.81.248:3307/mybatis?serverTimezone=UTC"
userId="root"
password="xqm@123456"
/>
<!--生成对应的实体类
targetPackage:指定生成java文件的目录
targetProject:放在那个工程的哪个目录下
-->
<javaModelGenerator targetPackage="com.xqm.model" targetProject="src/main/java"/>
<!--SQL映射文件生成器
targetPackage:指定生成java文件的目录
targetProject:放在那个工程的哪个目录下
-->
<sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources"/>
<!--dao接口生成器-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.xqm.mapper" targetProject="src/main/java"/>
<!--指定要逆向生成的数据表
tableName:表名
domainObjectName:对象名
-->
<table tableName="items" domainObjectName="Items" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
<table tableName="orderdetail" domainObjectName="Orderdetail" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
<table tableName="orders" domainObjectName="Orders" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
<table tableName="users" domainObjectName="Users" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
<!--<table tableName="dept" domainObjectName="Dept" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>-->
</context>
</generatorConfiguration>
springboot集成mybatis
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shopping</groupId>
<artifactId>manage</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>manage</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!--mybatis自动生成代码插件,用完必须注释掉-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--引入mybatis分页插件 排除mybatis 否则会出现循环依赖的问题-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.6.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--MyBatis自动生成代码的插件和配置-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!-- 在控制台打印执行日志 -->
<verbose>true</verbose>
<!-- 重复生成时会覆盖之前的文件-->
<overwrite>true</overwrite>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8001
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://124.223.81.248:3307/mall_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: xqm@123456
mybatis:
# config-location:
type-aliases-package: com.shopping.manage.model
mapper-locations: classpath:mapper/*.xml
configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 驼峰命名法
map-underscore-to-camel-case: true
#logging:
# level:
# com.xqm.mybatis.mapper: debug
# file:
# name: log.txt
pagehelper:
auto-dialect: true
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
file:
localhost: E:\
运行逆向工程
maven-Plugins-mybatis-generator-mybatis-generator:generate-右击选择Run ‘manage[mybatis-gene…]’
Application文件
在启动类上新增@MapperScan(basePackages = “com.shopping.manage.mapper”)
package com.shopping.manage;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "com.shopping.manage.mapper")
public class ManageApplication {
public static void main(String[] args) {
SpringApplication.run(ManageApplication.class, args);
}
}