myBatis
https://mybatis.org/
环境:
- jdk1.8
- maven 3.6.1
- mysql 5.7
- IDEA
前置知识
- JDBC
- mysql
- java基础
- Maven
- Junit
1:简介
1.1:什么是myBatis
- 是一款优秀的持久层框架
- 支持定制化SQL,存储过程以及高级映射
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解来配置和映射原生类型,接口,和java的POJO(普通老式java对象)为数据库中的数据
maven仓库
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
1.2:持久化
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
2:使用MyBatis框架
思路:搭建环境–》导入MyBatis–》编写代码–》测试
!!!配置文件中出现中文注释要将文件头的encoding从UTF-8改为GBK
2.1:搭建环境
创建数据库
CREATE DATABASE `mybatis`;
USE `mybatis`;
create table `user`(
`id` int(20) not null PREPARE 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>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
!!!注意,maven打包的时候需要配置resources,防止无法访问配置文件
<build>
<resources>
<resource>
<!--目录-->
<directory>src/main/java</directory>
<!--包括-->
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--过滤-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
2.2:编写配置mybatis-config.xml
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.198.128:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 映射表位置-->
<mappers>
<mapper resource="com/phc/dao/mapper/UserMapper.xml"/>
</mappers>
</configuration>
2.3:编写dao层接口
public interface IUserDao {
List<User> getUserList();
}
2.4:编写映射表UserMapper.xml
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace是命名空间,是dao层接口的全限定位置 -->
<mapper namespace="com.phc.dao.IUserDao">
<!-- id为方法名,一定要一摸一样 resultType为结果类型-->
<select id="getUserList" resultType="com.phc.pojo.User">
select * from mybatis.user;
</select>
</mapper>
2.5:编写工具类,能够获得SqlSession对象
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
2.6:测试
@Test
public void test() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user.toString());
}
sqlSession.close();
}
3:CURD,对Mappre.xml的解析
调用了增删改之后都要提交事务
<mapper namespace="com.phc.dao.IUserMapper">
<select id="getUserList" resultType="com.phc.pojo.User">
SELECT * FROM mybatis.user;
</select>
</mapper>
3.1:namespace 命名空间
namespace的参数就i是 Dao/mapper 的全限定名称
3.2:select 标签
查询语句
-
id : namespace指向的接口下的方法
-
resultType : sql语句执行的返回值
-
parameterType :参数类型,如果需要传递参数给mapper.xml中的sql语句,使用这个属性
<select id="getUserById" resultType="com.phc.pojo.User" parameterType="int"> <!-- #{id}中的id必须要与方法中的参数名称相同 --> select * from mybatis.user where id = #{id} </select> User getUserById(int id);
3.3:intsert标签
添加数据
-
<insert id="addUser" parameterType="com.phc.pojo.User"> <!-- #{id}, #{name}, #{pwd} 中的参数都必须与pojo中的参数名称相同,也就是user的变量名称 --> insert into mybatis.user (id, name, pwd) value (#{id}, #{name}, #{pwd}) </insert> int addUser(User user);
-
注意在调用了增删改之后都要提交事务 sqlSession.commit();
- 方法定义时候可以返回int变量,mybatis会返回添加了多少条
3.4:update标签
修改数据
-
<update id="updateUser" parameterType="com.phc.pojo.User"> update mybatis.user set name = #{name}, pwd=#{pwd} where id = #{id}; </update> int updateUser(User user);
-
注意在调用了增删改之后都要提交事务 sqlSession.commit();
- 方法定义时候可以返回int变量,mybatis会返回添加了多少条
3.4:delete标签
删除数据
-
<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id}; </delete> int deleteUser(int id);
-
注意在调用了增删改之后都要提交事务 sqlSession.commit();
- 方法定义时候可以返回int变量,mybatis会返回添加了多少条
3.5:可能产生的错误
- 标签匹配错误
- resources绑定mapper,需要使用路径
- 配置文件必须符合规范
- NullPointerException 没有注册到资源
- 输出的xml文件中有乱码
- maven资源没有导出
3.6:万能的Map
如果字段过多 ,考虑使用map,或者注解
<insert id="addUser2" parameterType="map">
insert into mybatis.user (id, name, pwd) value (#{userId}, #{userName}, #{passwd})
</insert>
int addUser2(Map<String,Object> map);
@Test
public void test2_1(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("userId",6);
map.put("userName","三笠");
map.put("passwd","123456");
int res = mapper.addUser2(map);
if (res>0){
System.out.println("插入条数---》"+res);
}
//提交事务
sqlSession.commit();
sqlSession.close();
}
3.7:模糊查询
通配符最好在sql语句内写死,防止sql注入
<!-- 注意因为使用单引号将like内语句包起来了,需要使用$来应用参数,不能用# -->
<select id="getUserLike" resultType="com.phc.pojo.User">
SELECT *
FROM mybatis.user
where name like '%${value}%';
</select>
List<User> getUserLike(String value);
@Test
public void test_like() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
IUserMapper userDao = sqlSession.getMapper(IUserMapper.class);
List<User> userList = userDao.getUserLike("子");
for (User user : userList) {
System.out.println(user.toString());
}
sqlSession.close();
}
4:配置解析
4.1:核心配置文件
- mybatis-config.xml
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2:环境配置(environments)
MyBatis 可以配置成适应多种环境
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
environments default= 参数可以用于指定环境
transactionManager type= 用来指定事务的类型 JDBC 标准的数据库事务 / MANAGED 让容器管理事务的生命周期
dataSource type= 指定数据源类型 UNPOOLED无池连接 / POOLED有池连接 / JNDI 数据库由web服务器负责初始化
- dbcp c3p0 druid 连接池,连接用完后可以回收 ,让下一个用户使用的时候无需再次连接,将没用的连接给新来的
4.3:属性(properties)
通过properties属性来实现应用配置文件,引入外部配置文件后外部配置文件的优先级更高
编写db.properties文件
// &是&的转义符,如果在xml中配置是需要&的,但是在db.properties文件中无需转义符,直接使用&即可
driver=com.mysql.jdbc.Driver
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.198.128:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
在核心配置文件中引入
<properties resource="db.properties">
<!-- 这里也引入了同名password属性,但是优先走外部配置,所以不生效 -->
<property name="password" value="111111"/>
</properties>
修改dataSource
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
4.4:类型别名(typeAliases)
- 别名是为java类设置一个短的名字,配置的时候就无需使用全限定类名了
- 减少完全限定名的冗余
在mybatis-config.xml中配置了typeAliases 别名 后,可以直接在Mapper.xml中使用别名
<typeAliases>
<!-- 直接对类使用,给类设置别名 -->
<typeAlias type="com.phc.pojo.User" alias="User"/>
<!-- 对包名使用,回去扫描包,包下类名的驼峰名就是别名,也可以对类使用@Alias指定别名 -->
<package name="com.phc.pojo"/>
</typeAliases>
mapper中的语句配置,将返回类型从全限定类名改为了别名
<select id="getUserList" resultType="user">
SELECT *
FROM mybatis.user;
</select>
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
4.5:设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
下表描述了设置中各项设置的含义、默认值等。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods )。 |
true | false | false (在 3.4.1 及之前的版本中默认为 true) |
multipleResultSetsEnabled | 是否允许单个语句返回多结果集(需要数据库驱动支持)。 | true | false | true |
useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE : 不做任何反应WARNING : 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN )FAILING : 映射失败 (抛出 SqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态 SQL 生成使用的默认脚本语言。 | 一个类型别名或全限定类名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) |
一个类型别名或全限定类名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) |
true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) |
true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) |
一个类型别名或完全限定类名。 | 未设置 |
shrinkWhitespacesInSql | Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5) | true | false | false |
重要的设置
- cacheEnabled 缓存是否开启
- lazyLoadingEnabled 懒加载
- useGeneratedKeys 允许jdbc支持自动生成主键
- mapUnderscoreToCamelCase 开启驼峰命名规则(phc_name映射为phcName)
- logImpl 指定myBatis的日志实现,未指定自动查找
4.6:映射器(Mappers)
<mappers>
<!-- 相对路径的资源引用 -->
<mapper resource="com/phc/dao/mapper/UserMapper.xml"/>
</mappers>
<mappers>
<!-- 使用class来绑定映射,但是映射文件必须和接口 文件同名 同路径 -->
<mapper class="com.phc.dao.IUserMapper"/>
</mappers>
<mappers>
<!-- 使用扫描包来绑定映射,但是映射文件必须和接口 文件同名 同路径 -->
<package name="com.phc.dao"/>
</mappers>
5:生命周期和作用域
作用域 生命周期 是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 多次重建 SqlSessionFactory 被视为一种代码“坏习惯”
- 可以想象为 数据库连接池
- 可使用单例模式和静态单例模式
SqlSession
-
连接到连接池的一个请求
-
实例不是线程安全的,因此不能被共享,最佳作用域是请求或者方法作用域
-
用完就关闭,否则资源会被占用
-
下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}
6:解决属性名和字段名不一致的问题
因为是mybatis自动对pojo填充的数据,所以必须要字段名于属性名一样
可以使用以下方法解决
-
起别名,在sql语句中使用as将数据库字段别名设置为属性名 !!! 不推荐
<select id="getUserLike" resultType="User"> SELECT id, name, pwd as password FROM mybatis.user where name like '%${name}%'; </select> <!-- 字段样式 private int id; private String name; private String password; -->
-
使用resultMap标签
<resultMap id="UserMap" type="User" > <!-- column是列名,也就是字段名 property是属性名 ,使用这两个标签将二者联系起来--> <result column="pwd" property="password"/> </resultMap> <select id="getUserLike" resultMap="UserMap"> SELECT * FROM mybatis.user where name like '%${name}%'; </select>
6.1:结果映射
resultMap
元素是 MyBatis 中最重要最强大的元素。
它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来
7:日志工厂
在mybatis-config.xml中配置 来启动日志
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
需要导包
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
7.1:log4j
-
先用maven导包
-
编写log4j配置文件 log4j.properties
#将等级为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=[%p][%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/blog.txt 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>
- 就可以了,框架会自动找配置文件,无需手动让配置文件生效
在类中的使用
static Logger logger = Logger.getLogger(UserDaoTest.class);
logger.info("啥子哦");
在开头实例化这个对象,调用对象方法能输出日志
8:分页查询
数据量大的时候分页查询 减少数据的处理量
使用sql语句LIMIT分页
select *from user limit 0,4
第一个参数是开始的位置,第二个参数是数据条数
limit分页
<resultMap id="UserMap" type="User">
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserByLimit" resultMap="UserMap" parameterType="map">
select *
from mybatis.user
limit #{startIndex},#{pageSize};
</select>
//接口方法
List<User> getUserLike(String name);
//测试使用
@Test
public void testLimit() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
logger.info(user.toString());
}
sqlSession.close();
}
8:使用注解开发
8.1:面向接口编程
解耦,可拓展
接口的理解
- 定义(规范,约束)与实现(名实分离原则)的分离
- 接口的本身反映了系统设计人对系统抽象的理解
- 接口应有2类
- 对一个个体的抽象,对应为抽象体(abstract class)
- 对于一个个体某一方面的抽象 形成一个抽象面(interface)、
- 一个体可能有多个抽象面,抽象体与抽象面是有区别的
三个面向区别
- 考虑问题的时候,以对象为单位,考虑他的属性以及方法
- 设计问题的时候,以一个具体的流程(事务过程)为单位,考虑实现
- 接口设计与非接口设计是针对复用技术的,与面向对象不是一个问题,更多的是体现对系统整体的架构
8.2:使用注解开发
本质:反射机制实现 底层:动态代理
对接口中的方法使用注解
@Select("select * from user where id = #{id}")
User getUserById(int id);
调用该方法
@Test
public void selectUserById() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);
User user = mapper.getUserById(1);
logger.info(user.toString());
sqlSession.close();
}
<!-- 其实无需在xml中配置这一条,因为注解会对方法进行自动装配,写了注解的方法直接调用即可 -->
<mapper class="com.phc.dao.IUserMapper"/>
9:动态sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
-
if
<!-- 一定要注意传递进来的参数类型和字段的类型,不一样的话sql语句不能生成 --> <select id="getOneORAllUser" resultMap="UserMap" parameterType="map"> SELECT * from user where <!-- 在if中的语句无需使用#来获得,直接写参数名就行了 --> <if test="all == null"> true; </if> <if test="all != null"> id = #{all}; </if> </select>
- choose (when, otherwise)
- trim (where, set)
- foreach
- 详细参阅 https://mybatis.org/mybatis-3/zh/dynamic-sql.html
10:缓存
- 什么是缓存(Cache)
- 在内存中的临时数据
- 将用户经常查询过的数据放在缓存中,用户查询的时候就无需去磁盘上查询了,而是从缓存中找,提高查询效率
- 为什么使用缓存
- 减少和数据库的交互次数,减少系统开销,提高系统效率
- 什么样的数据能使用缓存
- 经常查询并且不经常改变的数据
10.1:mybatis缓存
- mybatis包含十分强大的缓存特性,可以很方便的定制和配置缓存
- mybatis系统中默认定义了两级缓存一级缓存和二级缓存
- 默认情况下,只有一级缓存开启
- 二级缓存需要手动开启和配置,是基于namespace的缓存
- 为了提高拓展性,mybatis定义了缓存接口Cache,可以使用该接口完成自定义二级缓存
10.2:一级缓存
-
一级缓存也叫本地缓存
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 如果需要获得相同数据,直接从缓存中拿,没必要查询数据库
尝试使用不同的SqlSession
@Test public void testGetOneORAllUser() { SqlSession sqlSession = MyBatisUtils.getSqlSession(); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); List<User> oneORAllUser = mapper.getOneORAllUser(null); for (User user : oneORAllUser) { logger.info(user.toString()); } logger.info("============使用不同的sqlSession第二次查询=========="); SqlSession sqlSession1 = MyBatisUtils.getSqlSession(); IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class); List<User> oneORAllUser1 = mapper1.getOneORAllUser(null); for (User user : oneORAllUser1) { logger.info(user.toString()); } sqlSession.close(); }
查询出来的结果是有两句sql的,也使用jdbc进行了两次连接
[DEBUG][org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection [DEBUG][org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1397616978. [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Preparing: SELECT * from user where true; [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Parameters: [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-<== Total: 8 [INFO][com.phc.dao.UserDaoTest]-User{id=1, name='雪之下雪乃', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=2, name='间桐樱', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=3, name='缠流子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=4, name='远坂凛', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=5, name='团子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=6, name='三笠', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=7, name='土间埋', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=8, name='伊莉雅', password='233333'} [INFO][com.phc.dao.UserDaoTest]-============使用不同的sqlSession第二次查询========== [DEBUG][org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection [DEBUG][org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1259769769. [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Preparing: SELECT * from user where true; [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Parameters: [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-<== Total: 8 [INFO][com.phc.dao.UserDaoTest]-User{id=1, name='雪之下雪乃', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=2, name='间桐樱', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=3, name='缠流子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=4, name='远坂凛', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=5, name='团子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=6, name='三笠', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=7, name='土间埋', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=8, name='伊莉雅', password='233333'}
使用同一个sqlSession就不会有两次连接,只会有一次连接,第二次查询从缓存中自动读取
配置 flushCache=”true” sqlSession就会自动刷新缓存,即使使用同一个sqlSession也是需要进行二次查询的
<select id="getOneORAllUser" resultMap="UserMap" parameterType="string" flushCache="true"/>
- 在同一个
SqlSession
中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据; - 不同的
SqlSession
之间的缓存是相互隔离的; - 用一个
SqlSession
, 可以通过配置使得在查询前清空缓存; - 任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。
10.3:二级缓存
- 二级缓存也叫全局缓存,一级缓存的作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称对应一个缓存区
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这会话对应的一级缓存就没了,但是我们想要的是会话关闭后,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,可以直接从二级缓存中获取
- 不同的mapper查出的数据会放在自己对应的缓存中
二级缓存存在于SqlSessionFactory 的生命周期中
步骤
-
在mybatis配置文件中配置二级缓存—全局开关
<settings> <!-- 默认为true --> <setting name="cacheEnabled" value="true"/> </settings>
-
配置二级缓存—分开关,在mapper.xml中配置
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
进行测试
@Test public void testGetOneORAllUser() { SqlSession sqlSession = MyBatisUtils.getSqlSession(); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); List<User> oneORAllUser = mapper.getOneORAllUser(null); for (User user : oneORAllUser) { logger.info(user.toString()); } //这里对一级缓存进行了关闭,下面找不到一级缓存,就会直接去二级缓存中找 sqlSession.close(); logger.info("============使用不同的sqlSession第二次查询=========="); SqlSession sqlSession1 = MyBatisUtils.getSqlSession(); IUserMapper mapper1 = sqlSession1.getMapper(IUserMapper.class); List<User> oneORAllUser1 = mapper1.getOneORAllUser(null); for (User user : oneORAllUser1) { logger.info(user.toString()); } sqlSession1.close(); //结果是从缓存中读取的数据,并没有对数据库进行二次查询 }
-
结果
[DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Preparing: SELECT * from user where true; [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-==> Parameters: [DEBUG][com.phc.dao.IUserMapper.getOneORAllUser]-<== Total: 8 [INFO][com.phc.dao.UserDaoTest]-User{id=1, name='雪之下雪乃', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=2, name='间桐樱', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=3, name='缠流子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=4, name='远坂凛', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=5, name='团子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=6, name='三笠', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=7, name='土间埋', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=8, name='伊莉雅', password='233333'} [DEBUG][org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@25b485ba] [DEBUG][org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 632587706 to pool. [INFO][com.phc.dao.UserDaoTest]-============使用不同的sqlSession第二次查询========== [DEBUG][com.phc.dao.IUserMapper]-Cache Hit Ratio [com.phc.dao.IUserMapper]: 0.5 [INFO][com.phc.dao.UserDaoTest]-User{id=1, name='雪之下雪乃', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=2, name='间桐樱', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=3, name='缠流子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=4, name='远坂凛', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=5, name='团子', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=6, name='三笠', password='123456'} [INFO][com.phc.dao.UserDaoTest]-User{id=7, name='土间埋', password='233333'} [INFO][com.phc.dao.UserDaoTest]-User{id=8, name='伊莉雅', password='233333'}
MyBatisPlus使用提示
逻辑删除
-
编写配置文件
mybatis-plus: global-config: db-config: id-type: auto logic-delete-value: 1 #配置逻辑删除的全局规则 logic-not-delete-value: 0
-
使用@TableLogic标志实体类的属性,可以在这个注解下配置单独的规则,单独规则优先于全局规则
package com.phc.phcstore.storeproduct.product.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Date; import java.util.List; import lombok.Data; /** * 商品三级分类 * * @author phcbest * @email phcbest2017@outlook.com * @date 2021-06-21 17:35:50 */ @Data @TableName("pms_category") public class CategoryEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 分类id */ @TableId private Long catId; /** * 分类名称 */ private String name; /** * 父分类id */ private Long parentCid; /** * 层级 */ private Integer catLevel; /** * 是否显示[0-不显示,1显示] * TableLogic注解 用于表示逻辑删除 */ @TableLogic(value = "1", delval = "0") private Integer showStatus; /** * 排序 */ private Integer sort; /** * 图标地址 */ private String icon; /** * 计量单位 */ private String productUnit; /** * 商品数量 */ private Integer productCount; /** * 子分类 * 将表字段设置为不存在 */ @TableField(exist = false) private List<CategoryEntity> children; }
-
调用mybatisplus中封装好的删除方法,删除只会修改逻辑位,不会物理删除