持久化就是将程序的数据在持久状态和瞬时状态转化的过程
为什么需要需要持久化?
Dao层,Service层,Controller层…
【优点】
简单易学
:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现灵活
:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求解除sql与程序代码的耦合
:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性提供映射标签,支持对象与数据库的orm字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供xml标签,支持编写动态sql
(循环拼接)<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
CREATE DATABASE `mybatis-study`;
USE `mybatis-study`;
CREATE TABLE `user` (
`id` INT NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user` ( `id`, `name`, `pwd` )
VALUES
( 1, '张三', '123456' ),
( 2, '李四', '123456' ),
( 3, '王五', '123456' )
<dependencies>
<!-- mybatis -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
配置数据库连接信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis-study?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
</environments>
<!-- 每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="org/dam/dao/xml/UserMapper.xml"/>
</mappers>
</configuration>
package org.dam.utils;
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;
/**
* @Author dam
* @create 2023/12/20 11:11
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用mybatis获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 可以从SqlSessionFactory中获得 SqlSession 的实例
* SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
* @return
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
package org.dam.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author dam
* @create 2023/12/20 11:13
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
package org.dam.dao;
import org.dam.entity.User;
import java.util.List;
/**
* @Author dam
* @create 2023/12/20 11:15
*/
public interface UserDao {
List<User> getUserList();
}
<?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">
<!-- namespace=绑定一个Dao/Mapper接口 -->
<mapper namespace="org.dam.dao.UserDao">
<!-- select查询语句,ID对应方法名 -->
<select id="getUserList" resultType="org.dam.entity.User">
select * from user
</select>
</mapper>
报错
/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=63592:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit5-rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit-rt.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/cat.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/crs-agent.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/lib/rt.jar:/Users/dam/Projects/study/mybatis-study/target/test-classes:/Users/dam/Projects/study/mybatis-study/target/classes:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/org/mybatis/mybatis/3.5.1/mybatis-3.5.1.jar:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/junit/junit/4.12/junit-4.12.jar:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/mysql/mysql-connector-java/8.0.29/mysql-connector-java-8.0.29.jar:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/com/google/protobuf/protobuf-java/3.19.4/protobuf-java-3.19.4.jar:/Users/dam/Dev/Maven/apache-maven-3.9.5/maven-repository/org/projectlombok/lombok/1.18.26/lombok-1.18.26.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 org.dam.UserDaoTest,test
java.lang.ExceptionInInitializerError
at org.dam.UserDaoTest.test(UserDaoTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in org/dam/dao/xml/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource org/dam/dao/xml/UserMapper.xml
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at org.dam.utils.MybatisUtils.<clinit>(MybatisUtils.java:24)
... 25 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource org/dam/dao/xml/UserMapper.xml
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 27 more
Caused by: java.io.IOException: Could not find resource org/dam/dao/xml/UserMapper.xml
at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:114)
at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:100)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:371)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119)
... 29 more
Process finished with exit code 255
原因应该是没有扫描到mapper文件,在pom文件里面添加如下配置
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
package org.dam.dao;
import org.dam.entity.User;
import java.util.List;
import java.util.Map;
/**
* @Author dam
* @create 2023/12/20 11:15
*/
public interface UserDao {
List<User> getUserList();
//根据id查询用户
User selectUserById(int id);
//添加一个用户
int addUser(User user);
//修改一个用户
int updateUser(User user);
//根据id删除用户
int deleteUser(int id);
}
<?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">
<!-- namespace=绑定一个Dao/Mapper接口 -->
<mapper namespace="org.dam.dao.UserDao">
<!-- select查询语句,ID对应方法名 -->
<select id="getUserList" resultType="org.dam.entity.User">
select * from user
</select>
<select id="selectUserById" resultType="org.dam.entity.User">
select * from user where id = #{id}
</select>
<insert id="addUser" parameterType="org.dam.entity.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="org.dam.entity.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
package org.dam;
import org.apache.ibatis.session.SqlSession;
import org.dam.dao.UserDao;
import org.dam.entity.User;
import org.dam.utils.MybatisUtils;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author dam
* @create 2023/12/20 11:18
*/
public class UserDaoTest {
@Test
public void test(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
//执行sql
//方式一getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
catch (Exception e){
e.printStackTrace();
}
finally {
//关闭SqlSession
sqlSession.close();
}
}
@Test
public void setSelectUserById() {
SqlSession session = MybatisUtils.getSqlSession(); //获取SqlSession连接
UserDao mapper = session.getMapper(UserDao.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
@Test
public void testAddUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserDao mapper = session.getMapper(UserDao.class);
User user = new User(5,"王五","zxcvbn");
int i = mapper.addUser(user);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserDao mapper = session.getMapper(UserDao.class);
User user = mapper.selectUserById(1);
user.setPwd("asdfgh");
int i = mapper.updateUser(user);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSqlSession();
UserDao mapper = session.getMapper(UserDao.class);
int i = mapper.deleteUser(5);
System.out.println(i);
session.commit(); //提交事务,重点!不写的话不会提交到数据库
session.close();
}
}
本来只是让用户查询自己的数据
select * from user where id = #{id}
但是如果用户传入的id是"1 or 1=1",拼接出来的sql就变成了如下样式,所有用户数据就被拿走了
select from user where id = 1 or 1=1
避免方式:对数据做严格校验
假设我们的实体类,或者数据库中的表,字段或者参数过多,我们可以考虑使用Map!
Map传递参数,直接在sql中取出key即可!【parameterType=“map”】
对象传递参数,直接在sql中取对象的属性即可!【parameterType=“Object’”】
//增加一个用户
int addUserT(Map<String,Object> map);
<insert id="addUserT" parameterType="map">
insert into user (id, name, pwd)
values (#{userid}, #{username}, #{password});
</insert>
@Test
public void addUserT() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid", 5);
map.put("username", "hello");
map.put("password", "1231231");
mapper.addUserT(map);
sqlSession.commit();
sqlSession.close();
}
查询“李”字,应该要查出两个人
List<User> getUserLike(String value);
<select id="getUserLike" resultType="org.dam.entity.User">
select *
from user
where name like #{name}
</select>
@Test
public void getUserLike() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userLike = mapper.getUserLike("%李%");
for (User user : userLike) {
System.out.println(user.toString());
}
sqlSession.close();
}
能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
Mybatis默认的事务管理器就是JDBC,默认连接池是POOLED
<transactionManager type="[ JDBC | MANAGED ]"/>
有三种内建的数据源类型,type=“[UNPOOLED|POOLED|JNDI]”
driver-这是JDBC驱动的Java类的完全限定名(并不是JDBC驱动中可能包含的数据源类)
url-这是数据库的JDBC URL地址。
username-登录数据库的用户名。
password-登录数据库的密码。
defaultTransactionIsolationLevel-默认的连接事务隔离级别。
defaultNetworkTimeout-The default network timeout value in milliseconds to wait for the database operation to complete
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
driver.encoding=UTF8
这将通过DriverManager..getConnection(url,driverProperties)方法传递值为UTE8的encoding属性给数据库驱动。
数据源也有很多第三方的实现,比如dbcp
,c3p0
,druid
等等….
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。接下来,我们可以优化我们的配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
<configuration>
<!--方式一:导入properties文件-->
<properties resource="db.properties"/>
<!--方式二:可以在原有文件的基础上,增加一些配置属性,
如db.properties文件中只有driver和url的配置,可在下方配置username和password-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<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>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
当然也可以写全,这样就不需要补充了。如果两个地方都写了,会优先使用外部的文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=root
<!--第一种方式:配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
设置完之后就不用写这么长的名字
<!--第二种方式:给整个包名取别名,这样当前包下面的类无需单独为其取别名了-->
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
MyBatis会在包名下面搜索需要的Java Bean,扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
别名包下的类直接用小写(第一个字母)来就行
@Alias("hello")
public class User {
}
可以设置什么
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="org/dam/dao/xml/UserMapper.xml"/>
</mappers>
<mappers>
<mapper resource="org.dam.dao.UserDao"/>
</mappers>
<mappers>
<package name="org.dam.dao"/>
</mappers>
生命周期和作用域 是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
select * from user where id = #{id}
可以看做select id,name,pwd from user where id = #{id}
,mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】字段一样的不需要映射
<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
</select>
定义resultMap的id
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
指定返回类型为resultMap的id
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
一对多和多对一就不会这么简单了,之后会使用到一些高级的结果集映射,association,collection这些
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settins>
开启日志之后,控制台有输出
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。 如果一个都未找到,日志功能就会被禁用。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/dam.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
//注意导包:org.apache.log4j.Logger
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void selectUser() {
logger.info("info:进入selectUser方法");
logger.debug("debug:进入selectUser方法");
logger.error("error: 进入selectUser方法");
}
思考:为什么需要分页?
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
写sql可以使用Limit实现分页
# 语法
SELECT * FROM table LIMIT stratIndex,pageSize
# 检索记录行 6-15
SELECT * FROM table LIMIT 5,10;
# 为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1
# 检索记录行 96-last
SELECT * FROM table LIMIT 95,-1;
# 如果只给定一个参数,它表示返回最大的记录行数目
# 检索前 5 个记录行,换句话说,LIMIT n 等价于 LIMIT 0,n。
SELECT * FROM table LIMIT 5;
<select id="selectUser" parameterType="map" resultType="user">
select * from user limit #{startIndex},#{pageSize}
</select>
//选择全部用户实现分页
List<User> selectUser(Map<String,Integer> map);
//分页查询 , 两个参数startIndex , pageSize
@Test
public void testSelectUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 第几页
int currentPage = 1;
// 每页显示几个
int pageSize = 2;
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("startIndex",(currentPage-1)*pageSize);
map.put("pageSize",pageSize);
List<User> users = mapper.selectUser(map);
for (User user: users){
System.out.println(user);
}
session.close();
}
mybatis最初配置信息是基于 XML,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建(总的来说,注解不够灵活,推荐还是使用XML)
//查询全部用户
@Select("select id,name,pwd password from user")
public List<User> getAllUser();
<!--使用class绑定接口-->
<mappers>
<mapper class="org.dam.dao.UserDao"/>
</mappers>
@Test
public void testGetAllUser() {
SqlSession session = MybatisUtils.getSession();
//本质上利用了jvm的动态代理机制
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.getAllUser();
for (User user : users){
System.out.println(user);
}
session.close();
}
//添加一个用户
@Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
int addUser(User user);
@Test
public void testAddUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "张三", "123456");
mapper.addUser(user);
session.close();
}
//根据id删除用
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id")int id);
@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(6);
session.close();
}
//修改一个用户
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(6, "李四", "zxcvbn");
mapper.updateUser(user);
session.close();
}
//根据id查询用户
@Select("select * from user where id = #{id}")
User selectUserById(@Param("id") int id);
@Test
public void testSelectUserById() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
package org.dam.entity;
import lombok.Data;
/**
* @Author dam
* @create 2024/1/2 17:12
*/
@Data
public class Student {
private int id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
private int tid;
}
package org.dam.entity;
import lombok.Data;
import java.util.List;
/**
* @Author dam
* @create 2024/1/2 17:12
*/
@Data //GET,SET,ToString,有参,无参构造
public class Teacher {
private int id;
private String name;
//一个老师多个学生
private List<Student> students;
}
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="org/dam/dao/xml/UserMapper.xml"/>
<mapper resource="org/dam/dao/xml/StudentMapper.xml"/>
<mapper resource="org/dam/dao/xml/TeacherMapper.xml"/>
</mappers>
也可以使用偷懒方式
<mappers>
<mapper resource="org/dam/dao/xml/*Mapper.xml"/>
</mappers>
一个教师对应多个学生,查询每个学生的时候,都将其教师查询出来
先给整个实体类的包取别名,后面使用实体类的时候方便,不需要写完整的引用
package org.dam.dao;
import org.dam.entity.Student;
import java.util.List;
/**
* @Author dam
* @create 2024/1/2 17:13
*/
public interface StudentMapper {
//获取所有学生及对应老师的信息
public List<Student> getStudents();
}
需求
:
获取所有学生及其对应老师的信息
思路
:
<?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="org.dam.dao.StudentMapper">
<select id="getStudents" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
</mapper>
【解析】
这里传递过来的id,只有一个属性的时候,下面可以写任何值
<!--association关联属性
property属性名(Student里面的teacher字段用来存储教师的信息)
javaType属性类型(Teacher类名,因为前面加了配置给实体类包下的所有类取别名,所以这里直接写teacher即可)
column在多的一方的表中的列名(根据tid来查询教师信息)-->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
association中column多参数配置:column="{key=value,key=value}"
,其实就是键值对的形式,key是传给下个sql的取值名称,value是当前sql的字段名
<association column="{param1=id,param2=name}" property="User" select="getUser"></association>
<select id="getUser" resultMap="UserMap" parameterType="java.util.Map">
SELECT * FROM user_table WHERE id = #{param1} and name = #{param2}
</select>
@Test
public void testGetStudents(){
SqlSession session = MybatisUtils.getSqlSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
输出
public List<Student> getStudents2();
先直接从两个表里面直接查询出结果,然后进行结果集的映射
<select id="getStudents2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.name tname
from student s,
teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
@Test
public void testGetStudents2(){
SqlSession session = MybatisUtils.getSqlSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents2();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
查询教师及其学生(使用集合来存储)
package org.dam.dao;
import org.dam.entity.Teacher;
/**
* @Author dam
* @create 2024/1/2 17:13
*/
public interface TeacherMapper {
//获取指定老师,及老师下的所有学生
public Teacher getTeacher(int id);
}
<?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="org.dam.dao.TeacherMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname , t.name tname, t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{id}
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
</mapper>
@Test
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSqlSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}
public Teacher getTeacher2(int id);
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="teacher">
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="students" javaType="ArrayList" ofType="student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="student">
select * from student where tid = #{id}
</select>
@Test
public void testGetTeacher2(){
SqlSession session = MybatisUtils.getSqlSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}
JavaType和ofType都是用来指定对象类型的
MyBatis的强大特性之一便是它的动态SQL。如果你有使用JDBC或其它类似框架的经验,你就能体会到根据不同条件拼接SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态SQL这一特性可以彻底摆脱这种痛苦。虽然在以前使用动态SQL并非一件易事,但正是MyBatis提供了可以被用在任意SQL映射语句中的强大的动态SQL语言得以改进这种情形。动态SQL元素和JSTL或基于类似XML的文本处理器相似。在MyBatis之前的版本中,有很多元素需要花时间了解。MyBatis3大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis采用功能强大的基于OGNL的表达式来淘汰其它大部分元素。
CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
package org.dam.entity;
import lombok.Data;
import java.util.Date;
/**
* @Author dam
* @create 2024/1/3 10:57
*/
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
}
注册mapper到配置文件中
//新增一个博客
int addBlog(Blog blog);
<insert id="addBlog" parameterType="blog">
insert into blog (id, title, author, create_time, views)
values (#{id},#{title},#{author},#{createTime},#{views});
</insert>
package org.dam.utils;
import java.util.UUID;
/**
* @Author dam
* @create 2024/1/3 10:56
*/
public class IDUtil {
public static String genId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
@Test
public void addInitBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtil.genId());
blog.setTitle("Mybatis如此简单");
blog.setAuthor("狂神说");
blog.setCreateTime(new Date());
blog.setViews(9999);
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Java如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("Spring如此简单");
mapper.addBlog(blog);
blog.setId(IDUtil.genId());
blog.setTitle("微服务如此简单");
mapper.addBlog(blog);
// 提交事务
session.commit();
session.close();
}
插入数据成功
List<Blog> queryBlogIf(Map map);
如果作者名字为空,那么只根据博客名字模糊查询,反之,则根据博客名和作者名一起来查询
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog where
<if test="title != null">
title like #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
数据表的数据如下:
@Test
public void testQueryBlogIf(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","%如此简单%");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
模糊查询出了四条数据
@Test
public void testQueryBlogIf(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","%如此简单%");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
加入作者之后,只能查出两条数据
如果title为空呢?
@Test
public void testQueryBlogIf(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
// map.put("title","%如此简单%");
map.put("author","狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
会出现sql报错,因为where后面直接就连接了and,出现这种问题可以使用下面的WHERE语句来处理
使用where标签包裹之后,如果查询条件是and或or开头的话,就会被自动剔除
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title like #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
@Test
public void testQueryBlogIf() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
// map.put("title","%如此简单%");
map.put("author", "狂神说");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
List<Blog> queryBlogChoose(Map map);
otherwise标签是可以省略的,按照自己需要的逻辑来写就行
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog where
<choose>
<when test="title != null">
title like #{title}
</when>
<when test="author != null">
author = #{author}
</when>
<otherwise>
views > #{views}
</otherwise>
</choose>
</select>
@Test
public void testQueryBlogChoose() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title", "%如此简单%");
map.put("author", "狂神说");
map.put("views", "1");
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
@Test
public void testQueryBlogChoose() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("author", "狂神说");
map.put("views", "1");
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
@Test
public void testQueryBlogChoose() {
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("views", "1");
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
int updateBlog(Map map);
<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id};
</update>
@Test
public void testUpdateBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","动态SQL");
map.put("author","老三");
map.put("id","4e59cf171b6249d98848af3d64ba1350");
mapper.updateBlog(map);
session.commit();
session.close();
}
假如author为空的话,set标签会将 title = #{title},
最后面的,剔除
@Test
public void testUpdateBlog(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title","动态SQL");
// map.put("author","老三");
map.put("id","4e59cf171b6249d98848af3d64ba1350");
mapper.updateBlog(map);
session.commit();
session.close();
}
有时候某段sql可能需要被重复使用,为了简化代码,可以复用这段sql
将可复用的sql片段封装起来
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
使用sql片段
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
注意:
动态SQL的另外一个常用的操作需求是对个集合进行遍历,通常是在构建in
条件语句的时候
foreach元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。注意你可以将任何可迭代对象(如List、Set等)、Map对象或者数组对象传递给foreach作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用Map对象(或者Map.Entry对象的集合)时,index是键,item是值。
List<Blog> queryBlogForeach(Map map);
【参数介绍】
希望通过遍历,拼接出如下sql,select * from blog where 1=1 and (id=1 or id=2 or id=3)
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@Test
public void testQueryBlogForeach(){
SqlSession session = MybatisUtils.getSqlSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
List<String> ids = new ArrayList<String>();
ids.add("4e59cf171b6249d98848af3d64ba1350");
ids.add("3767f2aded624958965e817019706b4c");
ids.add("ccf4063d396a4fb4a9fcfdb055e2de38");
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
【运行结果】
[org.dam.dao.BlogMapper.queryBlogForeach]-==> Preparing: select * from blog WHERE ( id=? or id=? or id=? )
[org.dam.dao.BlogMapper.queryBlogForeach]-==> Parameters: 4e59cf171b6249d98848af3d64ba1350(String), 3767f2aded624958965e817019706b4c(String), ccf4063d396a4fb4a9fcfdb055e2de38(String)
[org.dam.dao.BlogMapper.queryBlogForeach]-<== Total: 3
Blog(id=4e59cf171b6249d98848af3d64ba1350, title=动态SQL, author=老三, createTime=Wed Jan 03 11:04:20 CST 2024, views=0)
Blog(id=3767f2aded624958965e817019706b4c, title=Spring如此简单, author=狂神说1, createTime=Wed Jan 03 11:04:20 CST 2024, views=0)
Blog(id=ccf4063d396a4fb4a9fcfdb055e2de38, title=微服务如此简单, author=狂神说1, createTime=Wed Jan 03 11:04:20 CST 2024, views=9999)
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存
和二级缓存
一级缓存也叫本地缓存:SqlSession
<select id="selectUserById" resultType="org.dam.entity.User">
select * from user where id = #{id}
</select>
@Test
public void testCache1() {
SqlSession session = MybatisUtils.getSqlSession();
UserDao mapper = session.getMapper(UserDao.class);
User user1 = mapper.selectUserById(1);
User user2 = mapper.selectUserById(1);
System.out.println(user1==user2);
session.close();
}
sqlSession.clearCache()
)一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!一级缓存就是一个Map。
<setting name="cacheEnabled" value="true"/>
简单配置
<cache/>
如果想要更精细化地定义缓存,可以使用类似下面的写法。表示:配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
【参数解读】
LRU-最近最少使用(Mybatis默认)
:移除最长时间不被使用的对象FTFO-先进先出
:按对象进入缓存的顺序来移除它们S0FT-软引用
:基于垃圾回收器状态和软引用规则移除对象WEAK-弱引用
:更积极地基于垃圾收集器状态和弱引用规则移除对象提示
二级缓存是事务性的。这意味着,当SqlSession完成并提交时,或是完成并回滚,但没有执flushCache=true的insert/delete/update语句时,缓存会获得更新。
@Test
public void testCache2(){
SqlSession session = MybatisUtils.getSqlSession();
SqlSession session2 = MybatisUtils.getSqlSession();
UserDao mapper = session.getMapper(UserDao.class);
UserDao mapper2 = session2.getMapper(UserDao.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
User user2 = mapper2.selectUserById(1);
System.out.println(user2);
System.out.println(user==user2);
session2.close();
}
如果想要在项目更好地使用缓存,建议去学习Redis
本文章为本人学习 遇见狂神说 的学习笔记,文章中大部分内容来源于狂神的视频(【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对狂神的优质课程表示感谢。