如何获得Mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
数据持久化
为什么需要持久化?
Dao层、Service层、Controller层.....
帮助我们将数据存入到数据库中
方便
传统的JDBC代码太复杂了。简化。框架。自动化。
不用Mybatis也可以。更容易上手。技术没有高低之分
优点
最重要的一点:使用的人很多!
思路:数据库表创建-->项目搭建导包-->配置文件编写-->编写工具类-->编写代码-->测试!
-- 创建一个数据库
CREATE DATABASE `mybatis`;
-- 切换到创建的数据库
USE `mybatis`;
-- 创建一张表
CREATE TABLE `user`(
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(20),
`password` VARCHAR(20)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 插入几条数据
INSERT INTO `user`(`id`, `name`, `password`)
VALUES(1, '怀旧', '123456'),
(2, '小旧', '654321'),
(3, '小旧', 'root');
-- 查询当前数据
SELECT * FROM `user`;
项目创建完成编写pom.xml文件导入所需依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
确保包都成功下载下来了
在resources目录下创建一个mybatis的配置文件(文件名建议 : 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>
<!-- 配置链接环境(可以配置多套) -->
<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>
<!-- 第二组测试-在使用的时候需要写id的名称就可以完成数据库的切换 -->
<environment id="test">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${test.driver}" />
<property name="url" value="${test.url}" />
<property name="username" value="${test.username}" />
<property name="password" value="${test.password}" />
</dataSource>
</environment>
</environments>
</configuration>
创建正式使用的环境--修改配置中的连接地址和用户名等信息
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<!-- 在xml配置文件中,& 符号需要使用 & 来转义 -->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8&serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
代码结构:
编写工具类代码(获取数据库连接对象)
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 java.io.IOException;
import java.io.InputStream;
// 因为需要操作的步骤都是固定的,需要要写
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
// 使用mybatis工具必须执行的以下三步
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接的对象
* @return SqlSession 对象(等价与 JDBC 的 Connection)
*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
编写实体类(与数据库的user表对应):
package com.huaijiuwang.pojo;
public class User {
private int id;
private String name;
private String password;
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String toString() {
return "User{id = " + id + ", name = " + name + ", password = " + password + "}";
}
}
编写dao层的接口:
package com.huaijiuwang.dao;
import com.huaijiuwang.pojo.User;
import java.util.List;
public interface UserDAO {
// 获取所有的用户数据
List<User> getUserList();
}
创建和UserDAO映射操作数据库的xml配置文件
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace绑定一个对应的dao,让配置文件和接口能够对应上 -->
<mapper namespace="com.huaijiuwang.dao.UserDAO">
<!-- 要编写查询语句就直接写select标签 里面id对应要dao中的方法名,resultType对应到返回值的类型,这边必须写全类名 -->
<select id="getUserList" resultType="com.huaijiuwang.pojo.User">
select * from `user`;
</select>
</mapper>
使用junit测试
注意:建议测试的包最好和需要测试的包一一对应
public class UserDAOTest {
@Test
public void testUserDAO(){
// 1. 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 2. 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 3. 通过 userDAO 执行查询获取数据
List<User> userList = userDAO.getUserList();
// 4. 输出查询结果
System.out.println(userList);
// 5. 最后记得关闭 sqlSession
sqlSession.close();
}
}
运行报错:
注意:每一个 xxxMaper.xml 文件都需要在我们的mybatis-config.xml 文件中注册不然识别不到
<!-- 每一个mapper.xml文件都需要在下面进行注册,不然不能使用 -->
<mappers>
<!-- 配置 UserDAO 注册 resource 写对应mapper.xml的全路径(以 / 分割,不能使用 . 来写了) -->
<mapper resource="com/huaijiuwang/dao/UserMapper.xml" />
</mappers>
运行再次报错,报错说没有当前的UserMapper.xml文件
查看打包的 target 目录
配置的xml存在,但是写在java目录下的xml文件没有导入进来
解决方案两种:
方式一解决:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<!-- 添加后就可以扫码到.xml文件了 -->
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
配置完成后,xml文件成功被加载了;
再次运行输出如下:
成功查询到数据库中的数据了;
方式二解决:
修改xml文件的位置
注意:
运行测试:
成功输出结果
输出目录加载的位置也没有问题;
第二种执行sql的方法(不建议使用)
public void testUserDAO(){
// 1. 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 2. 直接获取数据(不推荐默认返回 Object 对象需要强转)
List<Object> userList = sqlSession.selectList("com.huaijiuwang.dao.UserDAO.getUserList");
// 输出打印查询结果
System.out.println(userList);
// 3. 关闭 sqlSession
sqlSession.close();
}
namespace 中的包名要喝 DAO 接口的包名一致!
在编写一个,通过id查询用户数据
// 根据 id 获取用户数据
User getUserById(int id);
编写对应xml文件
<select id="getUserById" parameterType="int" resultType="com.huaijiuwang.pojo.User">
select * from `user` where `id` = #{id}
</select>
注意:
id: 需要对应到接口中的方法名
parameterType: 传递进来的参数的名称
resultType : 返回数据的类型
#{ xxx }: 用来取传入参数的值对应到方法中的参数名称,需要一样
例如修改接口为 User getUserById(int id1); 那么查询语句就需要修改为 select * from user
where id
= #{id1}
编写测试代码:
@Test
public void testUserDAO(){
// 1. 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 2. 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 3. 通过 userDAO 执行查询获取数据
User user = userDAO.getUserById(1);
// 4. 输出查询结果
System.out.println(user);
// 5. 最后记得关闭 sqlSession
sqlSession.close();
}
修改查询id为2的
// 3. 通过 userDAO 执行查询获取数据
User user = userDAO.getUserById(2);
// 插入一条用户数据
int addUser(User user);
<insert id="addUser" parameterType="com.huaijiuwang.pojo.User">
insert into `user` values(#{id}, #{name}, #{password})
</insert>
和这个对应
@Test
public void testUserDAO(){
// 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 插入一条数据
int i = userDAO.addUser(new User(4, "小明", "123321"));
// 判断数据是否成功插入
if(i > 0){
System.out.println("插入数据成功!");
}else{
System.out.println("插入数据失败!");
}
// 通过 userDAO 执行查询获取数据
List<User> userList = userDAO.getUserList();
// 输出查询结果
userList.forEach(System.out::println);
// 关闭 sqlSession
sqlSession.close();
}
注意这边会有问题:我们查看数据库
发现数据并没有插入到数据库中
原因是sql执行没有提交:手动配置提交插入(一定要在关闭连接和执行插入中间提交)
// 提交修改
sqlSession.commit();
// 关闭 sqlSession
sqlSession.close();
再次运行,数据库数据成功添加进去了
和插入一样直接上代码:
// 修改用户数据
int updateUser(User user);
<update id="updateUser" parameterType="com.huaijiuwang.pojo.User">
update `user` set `name` = #{name}, `password` = #{password} where `id` = #{id}
</update>
@Test
public void testUserDAO(){
// 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 插入一条数据
int i = userDAO.updateUser(new User(2, "小怀", "123321"));
// 判断数据是否成功插入
if(i > 0){
System.out.println("修改数据成功!");
}else{
System.out.println("修改数据失败!");
}
// 通过 userDAO 执行查询获取数据
List<User> userList = userDAO.getUserList();
// 输出查询结果
userList.forEach(System.out::println);
// 提交修改
sqlSession.commit();
// 关闭 sqlSession
sqlSession.close();
}
成功修改;
// 根据用户 id 删除一个用户
int deleteUserById(int id);
<delete id="deleteUserById" parameterType="int">
delete from `user` where `id` = #{id}
</delete>
@Test
public void testUserDAO(){
// 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 插入一条数据
int i = userDAO.deleteUserById(4);
// 判断数据是否成功插入
if(i > 0){
System.out.println("删除数据成功!");
}else{
System.out.println("删除数据失败!");
}
// 通过 userDAO 执行查询获取数据
List<User> userList = userDAO.getUserList();
// 输出查询结果
userList.forEach(System.out::println);
// 提交修改
sqlSession.commit();
// 关闭 sqlSession
sqlSession.close();
}
删除数据成功;
/**
* 获取数据库连接的对象
* @return SqlSession 对象(等价与 JDBC 的 Connection)
*/
public static SqlSession getSqlSession(){
// 参数设置为true,代表事务默认自动提交(默认为false)
return sqlSessionFactory.openSession(true);
}
例如现在的需求是需要修改一个用户,根据 id 修改一个用户,但是id同时可以被修改,就意味着需要传一个用户对象,还需要一个id,需要多个参数;
解决方案,使用Map对象来实现,
编写接口:
// 根据 id 修改用户数据,id也可以被修改
int updateUserById(Map<String, Object> map);
传入的参数为map类型就可以了
编写mapper.xml
<update id="updateUserById" parameterType="map">
update `user` set `id` = #{newId}, `name` = #{name}, `password` = #{password} where `id` = #{oldId}
</update>
参数为map对象的时候,需要编写 parameterType 为小写 map 类型,获取数据的时候,需要写的是map中的键就可以了;
@Test
public void testUserDAO(){
// 获取 SqlSession 对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 通过 sqlSession 对象获取DAO对象用来执行sql
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 创建一个map对象用来存修改参数
HashMap<String, Object> map = new HashMap<>();
map.put("oldId", 2);
map.put("newId", 10);
map.put("name", "怀旧111");
map.put("password", "asdfg");
// 插入一条数据
int i = userDAO.updateUserById(map);
// 判断数据是否成功插入
if(i > 0){
System.out.println("修改数据成功!");
}else{
System.out.println("修改数据失败!");
}
// 通过 userDAO 执行查询获取数据
List<User> userList = userDAO.getUserList();
// 输出查询结果
userList.forEach(System.out::println);
// 提交修改
sqlSession.commit();
// 关闭 sqlSession
sqlSession.close();
}
成功修改数据;
如果不使用map也可以解决,例如目前这个情况,我们其实可以在加一个实体类,
public class User1 {
private int newId;
private int oldId;
private String name;
private String password;
}
在传入的参数类型就可以写User1这个对象了;
或者使用注解方式来实现....
编写一个配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456
在 mybatis-config.xml 引入外部配置文件:
<!-- 引入外部配置文件 -->
<properties resource="db.properties" />
引入后就可以将我们dataSource里面的属性值换为引入的配置文件的值了:
<!-- 配置链接环境(可以配置多套) -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<!-- 在xml配置文件中,& 符号需要使用 & 来转义 -->
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
测试之前运行的代码:
还有一种方式,可以不引入外部的
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8&serverTimezone=UTC
删除配置文件中的用户名密码,在配置文件中写
<!-- 引入外部配置文件 -->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
测试运行:
同样运行成功!
注意:当两个地方存在同样的一个键的时候,优先使用的是外部引入的键的值.
使用测试:
为User类创建别名
<!-- 给类取别名 -->
<typeAliases>
<typeAlias alias="User" type="com.huaijiuwang.pojo.User" />
</typeAliases>
原始mapper.xml文件
<select id="getUserList" resultType="com.huaijiuwang.pojo.User">
select * from `user`;
</select>
修改后:
<select id="getUserList" resultType="User">
select * from `user`;
</select>
运行测试:
执行依然没问题;
去掉别名测试:
运行报错,说明能查出来是别名起效果了;
编写方式二:
通过包配置,包下面的所有类添加别名(最常用),它默认取的别名就是类的名称(小写大写都可以):
<!-- 给类取别名 -->
<typeAliases>
<package name="com.huaijiuwang.pojo"/>
</typeAliases>
<select id="getUserList" resultType="user">
select * from `user`;
</select>
运行测试:
依然没问题;
建议:在类比较多的时候使用方式二,类比较少可以使用方式一;
区别:第一种可以自定义名称,但是第二种不行,必须系统给什么名称就是什么名称(但也不完全是,后面可以通过注解来实现);
方式一:原始方式(配置xml文件路径)【推荐使用】
<mappers>
<!-- 配置 UserDAO 注册 resource 写对应mapper.xml的全路径(以 / 分割,不能使用 . 来写了) -->
<mapper resource="com/huaijiuwang/dao/UserMapper.xml" />
</mappers>
方式二:配置类绝对路径方式
<mappers>
<mapper class="com.huaijiuwang.dao.UserDAO" />
</mappers>
运行报错
原因:名称没有修改为一样的
必须修两个文件名相同:
注意点:
方式三:扫描包的方式【其次使用(当包很多的情况下)】
<mappers>
<package name="com.huaijiuwang.dao"/>
</mappers>
注意点和方式二一样,两个点都需要注意
在一般项目使用的时候,很多也会使用第三种情况:
默认的配置如下:
直接将接口包写成mapper,然后在里面创建接口以Mapper结尾,然后我们的xml配置文件就写和接口,相同名称。
这里的每一个Mapper,就代表一个具体的业务!
修改实体类
public class User {
private int id;
private String userName;
private String passWord;
... 省略其他代码
}
数据库的字段名:
发现不能够和实体类的字段对应上,那么现在我们在运行一下之前的代码:
发现查出来的数据;id 和 passWord 能够查出来,打死你userName查不出来了,
原因是id和数据库是能够对应起来的,然后passWord 虽然大小写不一样,但是里面的单词还是相同所以可以识别,我们再次改变属性名:
public class User {
private int id;
private String userName;
private String passWord1;
...........
}
再次测试运行:
分析问题:
我们执行的sql如下:
select * from `user`;
-- 其实可以 == 如下
select `id`, `name`, `password` from `user`;
在mybatis中会通过查出来的字段名取和java实体类的属性取匹配,匹配上了就可以将数据复制给实体类了,但是这边我们修改了实体类,name属性去找userName发现找不到所以就没有成功进行复制,我们可以调整sql让查出来的字段对应到实体类,就可以查出数据了;
<select id="getUserList" resultType="user">
select `id`, `name` userName, `password` passWord1 from `user`;
</select>
再次查询:
发现数据成功获取,但是这种方式相对来说还是比较麻烦,而且每次查询的数据都要去写一次别名,非常麻烦
然后我们就可以通过mybatis提供的resultMap来进行处理;
首先修改查询sql的返回值类型
<select id="getUserList" resultMap="userResult">
select * from `user`;
</select>
resultMap就是我们需要返回的数据对象类型,但是这边没有一个叫做 userResult 的东西,所以这个东西就需要我们自己来创建
我们可以创建一个resultMap属性,里面就可以设置一个id值,在我们的select语句的返回对象就可以使用resultMap来进行返回,内容就是我们定义的resultMap的id值;
上面的type就是写当前需要转换后的实体类的名称;例如之前的select的返回值resultType值,就是我们的User类
<resultMap id="userResult" type="user">
</resultMap>
里面的属性
目前就使用id、result就可以
<resultMap id="userResult" type="user">
<id property="id" column="id" />
<result property="userName" column="name" />
<result property="passWord1" column="password" />
</resultMap>
里面主要就是一个对应数据库查出来的字段--一个对应到java实体类名。
测试:
查询成功;
例如现在替换了实体类名称:
public class User {
private int id;
private String username111;
private String password222;
......
}
修改xml文件:
<resultMap id="userResult" type="user">
<id property="id" column="id" />
<result property="username111" column="name" />
<result property="password222" column="password" />
</resultMap>
数据同样能查出来;
<settings>
<!-- 配置日志输出 注意 logImpl 大小写不能错 两个值都不能错一个单词,不能多一个空格 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
运行查询代码查看输出:
注意:使用Log4J需要先导入Log4J所依赖的包,不然会报错!
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
修改配置文件为Log4J输出
<settings>
<!-- 配置日志输出 注意 logImpl 大小写不能错 两个值都不能错一个单词,不能多一个空格 -->
<setting name="logImpl" value="Log4J"/>
</settings>
测试运行:
发现报错了!
解释: 这条警告信息来自Log4j日志框架,表示没有为名为
org.apache.ibatis.logging
的日志记录器找到任何日志追加器(appender)。Log4j用追加器指定日志信息要输出到哪里,比如控制台、文件等。如果没有配置追加器,那么日志信息就不会被输出。解决方法:
- 确保在项目中包含了Log4j的配置文件,如
log4j.properties
或log4j.xml
。- 在配置文件中为
org.apache.ibatis.logging
logger配置一个或多个追加器,并指定日志级别。例如,在
log4j.properties
中添加如下配置:propertieslog4j.logger.org.apache.ibatis.logging=DEBUG, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
这个配置将为
org.apache.ibatis.logging
logger设置了输出到控制台的追加器,并将日志级别设置为DEBUG。请根据你的具体需求调整配置,例如更改日志级别或追加器的类型。如果你不需要日志信息,也可以选择忽略这个警告。
添加一个配置文件
log4j.properties
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
再次运行:
其他自行了解..........
为什么要分页?
-- 语法:select * from limit 起始位置, 查多少条
-- 当只有一个参数: select * from limit n; 意味着后面其实是 [0, n]
使用Mybatis实现分页:
先在数据库中多添加几条数据:
// 分页查询用户的数据
List<User> getUserListByPage(Map<String, Object> map);
<select id="getUserListByPage" resultMap="userResult" parameterType="map">
select * from `user` limit #{pageStart}, #{pageSize}
</select>
// 封装查询对象
public List<User> getUserListByPage(int page, int pageSize){
// 获取链接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 获取mapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 封装查询数据
HashMap<String, Object> map = new HashMap<>();
// 添加分页起始数据
map.put("pageStart", (page-1) * pageSize); // 一页有 pageSize 条数据,现在查询第 page 页,就重第 (page-1) * pageSize 条数据开始查
// 添加每一个显示的数量
map.put("pageSize", pageSize);
// 开始查询
List<User> userListByPage = userMapper.getUserListByPage(map);
// 输出查询数据
userListByPage.forEach(System.out::println);
// 关闭数据库连接
sqlSession.close();
return userListByPage;
}
测试代码:
@Test
public void testUserDAO(){
// 测试调用 查询第 1 页数据, 每一页 3 条数据
List<User> userListByPage = getUserListByPage(1, 3);
// 输出数据
userListByPage.forEach(System.out::println);
}
测试查询第二页:
@Test
public void testUserDAO(){
// 测试调用 查询第 1 页数据, 每一页 3 条数据
List<User> userListByPage = getUserListByPage(2, 3);
// 输出数据
userListByPage.forEach(System.out::println);
}
不在使用sql实现分页了(不推荐使用,效率低,并且不安全,因为要使用sql的selectList方法,并不是通过类class实现)
// 使用RowBounds实现分页
List<User> getUserListByRowBounds();
<select id="getUserListByRowBounds" resultMap="userResult">
select * from `user`
</select>
@Test
public void testUserDAO(){
// 创建RowBounds对象
RowBounds rowBounds = new RowBounds(1, 3);// 这边的意思还是查询第 2 页, 每页 3 条数据
// 获取链接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 查询数据
List<User> userList = sqlSession.selectList("com.huaijiuwang.mapper.UserMapper.getUserListByRowBounds", null, rowBounds);
// 打印数据
userList.forEach(System.out::println);
// 关闭链接
sqlSession.close();
}
步骤如下:
第一步还是先编写接口
public interface UserMapper {
List<User> getUserList();
}
接下来就不是去配置mapper.xml文件了,直接在方法上面使用注解@Select
@Select("select * from `user`")
List<User> getUserList();
注解里面就写之前在mapper.xml中写的sql语句
第二步,在配置文件添加
<mappers>
<mapper class="com.huaijiuwang.mapper.UserMapper" />
</mappers>
在这边就不需要再添加mapper.xml的映射了
第三步,运行测试:
@Test
public void testUserDAO(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
userList.forEach(System.out::println);
sqlSession.close();
}
数据查询到了,但是这边还是有局限性,不能配置resultMap了,所以数据无法映射
但是可以使用之前的方式修改sql别名:
@Select("select id, name username111, password password222 from `user`")
List<User> getUserList();
完成查询
测试查询有参数的语句,同样可以
@Select("select id, name username111, password password222 from `user` where id = #{id}")
User getUserById(int id);
测试运行:
@Test
public void testUserDAO(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
查询成功,其他的增删改同理
所以在简单的增删改查的代码可以使用注解方式来实现,但是对于复杂的还是推荐使用xml配置的方式来实现;
例如一个需要多个参数的sql:
测试之前写法:
@Insert("update user set name = #{username111}, password = #{password222} where id = #{id}")
int update(User user);
修改用户数据测试:
@Test
public void testUserDAO(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.update(new User(1, "怀旧1", "1111"));
List<User> userList = mapper.getUserList();
userList.forEach(System.out::println);
sqlSession.close();
}
修改成功,但是现在还是之前的需求,需要修改到id的数据,所以就需要两个id数据,现在就可以使用注解@Param()注解来实现,修改代码
@Insert("update user set id= #{newId}, name = #{name}, password = #{password} where id = #{oldId}")
int update(@Param("oldId") int oldId, @Param("newId") int newId, @Param("name") String name, @Param("password") String password);
测试代码:
@Test
public void testUserDAO(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.update(10, 100, "哈哈哈", "33333");
List<User> userList = mapper.getUserList();
userList.forEach(System.out::println);
sqlSession.close();
}
修改成功!注意:当接口方法中存在多个参数就必须使用Param注解
提醒:
在编写sql里面的参数的时候我们一般使用的是 #{} 来进行参数传递,但是还有一种方式 ${} 他们二者的区别是 #{} 是预编译的可以防止sql注入而 ${} 是拼接的方式,不能访sql注入。
Lombok的使用可以提高代码的可读性、简洁性和可维护性,让开发者能够更专注于业务逻辑的实现。例如,通过@Data注解,Lombok可以在编译时自动生成get、set、equals、hash、toString等方法,避免写大量的代码,减少了代码量,也使代码看起来更加简洁。尤其对于一些对象属性需要改动的时候,每次改动都需要重新生成这些方法,而使用注解则可以避免此问题。
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
使用测试:
之前编写的实体类
public class User {
private int id;
private String username111;
private String password222;
public User() {
}
public User(int id, String username111, String password222) {
this.id = id;
this.username111 = username111;
this.password222 = password222;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername111() {
return username111;
}
public void setUsername111(String username111) {
this.username111 = username111;
}
public String getPassword222() {
return password222;
}
public void setPassword222(String password222) {
this.password222 = password222;
}
public String toString() {
return "User{id = " + id + ", username111 = " + username111 + ", password222 = " + password222 + "}";
}
}
现在的情况,加上注解:
自动生成了其这些方法;
其中还有很多其他的注解如下:
注解 | 注解作用 |
---|---|
@Getter | 自动生成字段的getter方法 |
@Setter | 自动生成字段的setter方法 |
@ToString | 自动生成toString方法 |
@EqualsAndHashCode | 自动生成equals和hashCode方法 |
@NoArgsConstructor | 自动生成无参构造函数 |
@AllArgsConstructor | 自动生成包含所有字段的构造函数 |
注解 | 注解作用 |
---|---|
@Data | 组合了@Getter、@Setter、@ToString、@EqualsAndHashCode等注解的功能 |
@Value | 组合了@Getter、@ToString、@EqualsAndHashCode等注解的功能 |
思考:@Data注解和@Value注解有哪些区别?
解答:@Data生成的类是可变的,具有可读写的setter方法,@Value生成的类是不可变的,字段是final的,没有生成setter方法。
注解 | 注解作用 |
---|---|
@Slf4j | 自动生成一个名为log的日志记录器 |
注解 | 注解作用 |
---|---|
@Builder | 自动生成Builder模式的构造器方法 |
@NonNull | 自动生成非空检查 |
@Delegate | 自动生成委托方法 |
@Cleanup | 自动释放资源 |
一般情况下使用最多的是如下的
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String username111;
private String password222;
}
一个所有的,一个无参,一个全参的;
多对一的关系在数据库中十分常见,例如:一个老师教很多学生,从老师的角度来是一对多的关系,而从学生的角度来看就是多对一的关系。
执行下面的sql:
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `teacher` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
insert into `teacher`(`id`,`name`) values (1,'怀旧老师');
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`teacher_id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `teacher_id` (`teacher_id`),
CONSTRAINT `student_ibfk_1` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
insert into `student`(`id`,`name`,`teacher_id`) values (2,'怀旧',1),(3,'小黄',1),(4,'小黑',1),(5,'小美',1),(6,'小甜',1);
现在是在数据库中创建了两张表,分别是学生表,和教师表,里面学生表中对应到教师的id,来构成多对一的连接关系.
文件结构如下,编写代码:
StudentMapper、TeacherMapper 为空接口
Student实体类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
private Teacher teacher;
}
Teacher实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
}
StudentMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huaijiuwang.mapper.StudentMapper">
</mapper>
TeacherMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huaijiuwang.mapper.TeacherMapper">
</mapper>
mybatis-config.xml 文件:
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部配置文件 -->
<properties resource="db.properties" />
<!-- 给类取别名 -->
<typeAliases>
<package name="com.huaijiuwang.pojo"/>
</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>
</environments>
<!-- 每一个mapper.xml文件都需要在下面进行注册,不然不能使用 -->
<mappers>
<mapper resource="mapper/StudentMapper.xml" />
<mapper resource="mapper/TeacherMapper.xml" />
</mappers>
</configuration>
配置完成,测试基本查询是否能够运行
编写一个接口查询教师数据
// 根据教师id查询教师数据
Teacher getTeacherById(int id);
编写mapper文件
<select id="getTeacherById" parameterType="int" resultType="teacher">
select * from teacher where id = #{id}
</select>
编写测试:
@Test
public void testUserDAO(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacherById(1);
System.out.println(teacher);
sqlSession.close();
}
数据查询成功,环境没有问题;
现在需要查询所有学生的数据,并且包含这个学生对应老师的数据
select * from student s, teacher t where s.teacher_id = t.id
数据查询成功
编写接口
// 查询所有学生数据
List<Student> getStudentList();
编写xml
<select id="getStudentList" resultType="student">
select * from student s, teacher t where s.teacher_id = t.id
</select>
编写测试:
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudentList();
studentList.forEach(System.out::println);
sqlSession.close();
结果中,学生的数据查询出来了,但是教师的数据还是为null,所以就需要修改我们的查询语句了;
在我们的mapper.xml中,返回的结果集中,返回的是Student实体类数据,但是查询出来的是一条一条的数据,没办法将教师的数据封装成一个对象赋值给student的教师属性,所以我们可以尝试新的方式,resultMap
<resultMap id="studentResult" type="student">
<id property="id" column="id" />
<result property="name" column="name" />
</resultMap>
<select id="getStudentList" resultMap="studentResult">
select * from student s, teacher t where s.teacher_id = t.id
</select>
我们知道id可以封装查询出来的一个主键,result可以封装一个字段,我们在看看里面还有其他的什么方法;
里面还有:association 和 collection ;
其实这两个对象就是用来将我们查询出来的数据进行封装处理的属性
association :可以封装一个对象
collection :可以封装一个对象集合
我们这边是需要一个教师对象,所以就可以使用association
在 association 也需要编写 property 用来对应到实体类的属性;
然后在 property 的里面可以在封装一个对象:案例如下
<resultMap id="studentResult" type="student">
<id property="id" column="id" />
<result property="name" column="name" />
<association property="teacher">
<id property="id" column="id" />
<result property="name" column="name" />
</association>
</resultMap>
运行测试:
发现数据查询出来了,但是教师的结果发生了错误!
这边发生这种情况是因为我们学生类和教师类的属性都一样,所以刚好查出来的数据对应上了,所以教师的数据成了学生的数据;
但是我们的sql语句查询出来的结果如下
想要查出来的数据能够对应上,我们可以直接修改查出来的数据的别名,让他有一个自己的标识
select s.id s_id, s.name s_name, t.id t_id, t.name t_name from student s, teacher t where s.teacher_id = t.id
这样我们在修改resultMap
<resultMap id="studentResult" type="student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
<association property="teacher" javaType="teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
</association>
</resultMap>
<select id="getStudentList" resultMap="studentResult">
select s.id s_id, s.name s_name, t.id t_id, t.name t_name from student s, teacher t where s.teacher_id = t.id
</select>
运行测试:
发现成功查询出来了数据;
还有一种写法
直接上mapper.xml代码
<resultMap id="studentResult" type="student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
<association property="teacher" column="teacher_id" javaType="teacher" select="getStudent" />
</resultMap>
<select id="getStudentList" resultMap="studentResult">
select * from student
</select>
<select id="getStudent" resultType="teacher" parameterType="int">
select * from teacher where id = #{teacher_id}
</select>
解释:
第一种方式来说,是通过多表查询一次性查出两张表的所有数据,然后直接将数据封装到实体类
第二种方式来说,是通过先查询学生的数据,然后根据查询出来的学生数据中的教师id在去查询一次教师数据,然后在封装到实体类中;可以理解为子查询。
二者比较:第一种方式效率会高一点,第二种方式更利于理解;
首先修改实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> studentList;
}
因为一个老师对应多个学生,所以这边需要创建一个属性用来存这一个老师对应的所有学生
需求:根据教师id查询教师的数据,并查询出这个教师的所有学生数据
编写接口
// 查询所有的教师数据
List<Teacher> getTeacherList();
编写mapper.xml
<select id="getTeacherList" resultType="teacher">
select * from teacher
</select>
测试代码:
SqlSession sqlSession = MybatisUtil.getSqlSession();
sqlSession.getMapper(TeacherMapper.class).getTeacherList().forEach(System.out::println);
sqlSession.close();
修改mapper.xml,查询多个学生
sql编写测试:
select t.id t_id, t.name t_name, s.id s_id, s.name s_name from teacher t inner join student s on t.id = s.teacher_id
查询结果如下,现在多个教师数据对应到了很多位学生数据了
我们编写xml
<resultMap id="teacherResult" type="teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
<collection property="studentList" ofType="student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
</collection>
</resultMap>
<select id="getTeacherList" resultMap="teacherResult">
select t.id t_id, t.name t_name, s.id s_id, s.name s_name from teacher t inner join student s on t.id = s.teacher_id
</select>
测试运行:
SqlSession sqlSession = MybatisUtil.getSqlSession();
for (Teacher teacher : sqlSession.getMapper(TeacherMapper.class).getTeacherList()) {
System.out.println(teacher);
teacher.getStudentList().forEach(System.out::println);
}
sqlSession.close();
成功查询出学生集合:注意,这边的collection属性,查询出俩的list数据需要写ofType类型,不然会报错。。。。
按照子查询的方式在查一次
修改mapper.xml
<resultMap id="teacherResult" type="teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
<collection property="studentList" column="id" ofType="student" select="getStudentsByTeacherId" />
</resultMap>
<select id="getTeacherList" resultMap="teacherResult">
select * from teacher
</select>
<select id="getStudentsByTeacherId" resultType="student" parameterType="int">
select * from student where teacher_id = #{id}
</select>
同样查询成功!
注意点:
MyBatis中的动态SQL是一种可以根据不同条件生成不同SQL语句的技术。它允许我们在映射文件中编写灵活的SQL语句,以便根据参数的不同情况来动态生成SQL语句。这种灵活性使得我们能够根据应用程序的需求来构建动态的查询语句。
动态SQL是根据不同条件和需求,动态生成SQL语句的一种技术。它的作用主要有以下几点:
条件灵活:使用动态SQL可以根据不同的条件生成不同的SQL语句,使得查询、更新或删除数据时能够根据具体情况进行灵活的处理。
查询优化:有时候在编写静态SQL语句时难以预料到查询条件的变化,而使用动态SQL可以根据运行时的条件动态调整查询语句,从而更好地适应实际情况,提高查询性能。
动态表名和字段名:有时候需要根据不同的场景来操作不同的表或字段,这时候就可以利用动态SQL来动态构建表名和字段名,实现灵活性和扩展性。
防止SQL注入:通过使用参数化查询或者绑定变量的方式来构建动态SQL,可以有效防止SQL注入攻击,提升系统的安全性。
常用标签 | 作用 |
---|---|
if | 根据指定的条件判断是否包含某部分SQL代码,使得SQL语句在运行时更具灵活性。 |
where | 生成动态的WHERE子句,只有满足条件时才包含WHERE子句,避免不必要的WHERE关键字。 |
choose | 根据不同的条件选择执行不同的SQL片段,实现类似于switch-case语句的功能。 |
foreach | 对集合进行循环,并在SQL语句中使用循环的结果,可以用于动态构建IN或VALUES子句。 |
set | 生成动态的SET子句,只有满足条件时才包含SET子句,用于动态更新表中的字段。 |
trim | 对SQL语句进行修剪和重组,去掉多余的AND或OR等,以便根据不同的条件动态生成合适的SQL语句。 |
查询一条学生数据(需求:传入一个学生信息,如果传入信息不为空,就查询对应字段比对的数据)
编写接口:
// 查询学生数据
List<Student> getStudentListByStudent(Student student);
编写mapper.xml
<select id="getStudentListByStudent" resultType="student" parameterType="student">
select * from student
where 1=1
<if test="id != 0">
and id = #{id}
</if>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
</select>
代码中where条件后面更上了一个 1 = 1,是为了避免后面的if里面要是成立了,但是前面没有数据,后面直接and语句会导致报错(但是这个问题后面可以解决)
这边是如果输入的数id不为0就根据id进行查询,如何用户名不为null或者空,就根据用户名进行模糊查询;
运行测试:
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student student = new Student();
// 没有数据说明查询所有用户数据
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
sqlSession.close();
Student student = new Student();
// 设置id值,查询
student.setId(1);
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
Student student = new Student();
// 设置id值,查询
student.setId(2);
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
Student student = new Student();
// 设置id值,查询
student.setName("小");
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
Student student = new Student();
// 设置id值,查询
student.setName("旧");
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
测试很成功!
其他标签学习可以参考网站:网页链接
<sql id="publicSql">
<if test="id != 0">
and id = #{id}
</if>
<if test="name != null and name != ''">
and name like concat('%', #{name}, '%')
</if>
</sql>
<select id="getStudentListByStudent" resultType="student" parameterType="student">
select * from student
where 1=1
<include refid="publicSql" />
</select>
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student student = new Student();
// 设置id值,查询
student.setName("旧");
sqlSession.getMapper(StudentMapper.class).getStudentListByStudent(student).forEach(System.out::println);
sqlSession.close();
运行同样没问题
一级缓存使用测试:
创建根据学生id查询学生数据:
// 根据学生id查询学生数据
Student getStudentById(@Param("id") int id);
<select id="getStudentById" resultType="student" parameterType="int">
select * from student where id = #{id}
</select>
使用测试:
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student student1 = sqlSession.getMapper(StudentMapper.class).getStudentById(2);
System.out.println(student1);
sqlSession.close();
测试默认开启的一级缓存:
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student student1 = sqlSession.getMapper(StudentMapper.class).getStudentById(2);
System.out.println(student1);
Student student2 = sqlSession.getMapper(StudentMapper.class).getStudentById(2);
System.out.println(student2);
System.out.println(student1 == student2);
sqlSession.close();
当在查询的途中,有了增删改操作,就会自动取刷新缓存:
// 修改学生数据
int updateStudentById(Student student);
<update id="updateStudentById" parameterType="student">
update student set name = #{name} where id = #{id}
</update>
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = mapper.getStudentById(2);
System.out.println(student1);
mapper.updateStudentById(new Student(2, "啊啊啊", new Teacher()));
Student student2 = mapper.getStudentById(2);
System.out.println(student2);
System.out.println(student1 == student2);
sqlSession.close();
测试可能遇到的问题:
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = mapper.getStudentById(2);
student1.setName("aaaa");
System.out.println(student1);
Student student2 = mapper.getStudentById(2);
System.out.println(student2);
sqlSession.close();
上面因为有缓存的存在,所以第一次获取数据后,数据被修改,第二次再次获取数据,但是查出来的数据是被修改后的数据,所以这时候在使用的时候就可能会出现bug,现在就需要使用手动关闭缓存来实现数据正常(clearCache 方法)。
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = mapper.getStudentById(2);
student1.setName("aaaa");
System.out.println(student1);
sqlSession.clearCache();
Student student2 = mapper.getStudentById(2);
System.out.println(student2);
sqlSession.close();
手动关闭缓存后,数据正常。
使用步骤:
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<mapper namespace="com.huaijiuwang.mapper.StudentMapper">
<!-- 配置可以使用二级缓存 -->
<cache />
自定义设置参数:
<!-- 配置可以使用二级缓存 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
还是用之前的例子,这次查询一次数据就关闭sqlSession然后在开一个,重新查询:
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
Student student1 = sqlSession1.getMapper(StudentMapper.class).getStudentById(2);
sqlSession1.close();
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
Student student2 = sqlSession2.getMapper(StudentMapper.class).getStudentById(2);
System.out.println(student1);
System.out.println(student2);
System.out.println(student1 == student2);
通过结果可以看出来,当前还是只查询了一次数据库,并且两次获取的数据是相同的。
关闭二级缓存在测试一下:
关闭后,就查询了两次数据库;
清理缓存的方式和一级缓存一模一样,并且也是数据被增删改才回刷新缓存;
问题:
关闭其他配置:
运行直接报错,原因是当前的实体类没有做序列号,数据无法从缓存取出后直接编译赋值为新的对象中;
解决办法,让实体类实现序列号接口即可;
public class Student implements Serializable {
实现序列号接口后,数据成功返回,并且查询数据库还是只进行了一次,但是数据对象不同了,这样就解决了缓存查询数据,导致第二次查询数据不一致的问题;
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存;
要在程序中使用ehcache,先要导包!
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
在mapper中指定使用我们的ehcache缓存实现!
<!--在当前Mapper.xml中使用二级缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
在Resource目录下新建ehcache.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>
注意:
评论
登录后才可以进行评论哦!