Mybatis学习笔记
1.1、什么是Mybatis
-
MyBatis 是一款优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
如何获得Mybatis?
-
Maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
1.2、持久层
数据持久化
-
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
-
内存:断电及失
-
数据库(Jdbc),io文件持久化
-
也可以用生活来举例:冷藏,罐头
为什么需要持久化
-
有一些对象,不能让它丢掉
-
内存的价格高昂
1.3、持久层
Dao层,Service层,Controller层
-
完成持久化工作的代码块
-
层界限十分明显
1.4、为什么需要Mybatis
-
帮助程序员将数据存入到数据库中
-
方便
-
简化JDBC的操作,自动化
-
不用Mybatis也可以,只是说使用了Mybatis会更容易上手
-
优点:
-
简单易学
-
灵活
-
sql和代码的分离,提高了可维护性。
-
提供映射标签,支持对象与数据库的orm字段关系映射
-
提供对象关系映射标签,支持对象关系组建维护
-
提供xml标签,支持编写动态sql
-
使用的人相对来说较多
-
2、第一个Mybatis程序
思路:搭建环境-->导入Mybatis-->编写代码-->测试
2.1、搭建环境
2.1.1:搭建数据库
CREATE DATABASE `Mybatis` if not EXISTS;
use `Mybatis`;
drop table `user` if EXISTS;
CREATE TABLE `USER`(
`id` INT(20) not null PRIMARY KEY AUTO_INCREMENT,
`name` varchar(30) DEFAULT null,
`pwd` VARCHAR(30) DEFAULT null,
`birthday` VARCHAR(30) DEFAULT '1900-01-01'
)ENGINE=INNODB DEFAULT CHARSET=utf8;
insert into `USER`(`name`,`pwd`,`birthday`) VALUES
('张三','zhangsan','2008-01-01'),
('李四','lisi','2010-01-01'),
('王五','wangwu','2003-01-01')
2.1.2、新建一个项目
这样项目就建立好了
我们再查看下我们Maven的环境,点击File-->Settings 找到Maven
如果这里是如上图显示的 则最好是修改为自己电脑的地址
下面的User settings file和Local repository无法换位置的记得勾选旁边的Override
删除src目录
导入Maven依赖(pom.xml)pom.xml中,未下载成功的依赖会变成红色:
2.1.3、创建一个模块
在主项目上右键->new->Module...
这样我们就可以看到 他这里是有两个pom.xml的 一个是总的 我们所有里面的项目都可以调用这个总的pom.xml 我们叫他为父工程
再看到这里的parent 我们可以看到 这个就是我最开始新建项目的名字 也就是他的父工程
在Resource目录下新建一个文件 名字为mybatis-config.xml
里面的代码如下:
我们再新建几个目录和一个MybatisUtil.java
文件,截图如下:
其中MybatisUtil.java的代码如下:
package com.april.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;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取resource目录下的mybatis-config.xml文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.2、编写实际性代码
实体类
在pojo目录下,新建一个User的java类,这个类是让你编写你数据库中数据的属性的,代码如下:
package com.april.pojo;
public class User {
private int id;
private String name;
private String pwd;
private String birthday;
public User() {
}
public User(int id, String name, String pwd, String birthday) {
this.id = id;
this.name = name;
this.pwd = pwd;
this.birthday = birthday;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPwd() {
return pwd;
}
public String getBirthday() {
return birthday;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
mapper接口
在mapper这个文件夹中 新建一个UserMapper的Interface类,代码如下:
package com.april.mapper;
import com.april.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> getUserList();
}
继续在mapper这个文件夹下面 新建一个UserMapper.xml文件 用于实现mapper接口
2.3、测试
注意点
-
Junit测试
这时候我们在test目录下新建一个和上方一样的路径,并新建一个UserMapperTest.java文件
在里面写上如下代码并运行:
package com.april.mapper;
import com.april.pojo.User;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
// 获得Sqlsession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 执行SQL 从sqlsession中获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 现在就可以直接从mapper中调用UserMapper的方法了 由于我们的xml文件的id直接绑定的是UserMapper的方法 所以我们直接调用内部的方法即可
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
// 关闭sqlsession
sqlSession.close();
}
}
这时候我们会发现一个报错org.apache.ibatis.binding.BindingException: Type interface com.april.mapper.UserMapper is not known to the MapperRegistry.
,原因是我们没有注册mappers
这时候我们只需要在我们的mybatis-config.xml中加上
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
合起来就是这个样子
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration:核心配置文件-->
<configuration>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
</configuration>
重新运行UserMapperTest会有java.lang.ExceptionInInitializerError
这是因为我们找不到我们的UserMapper.xml,因为我们java会生成class文件运行 所以需要我们target目录下有UserMapper.xml,需要在pom.xml文件中加上如下内容(建议以后所有的MAVEN项目先把这个加上):
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
合起来是这个样子:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>MyTestMybatis</artifactId>
<groupId>com.april</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>MybatisTest1</artifactId>
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
然后在命令行输入如下命令:mvn clean
和mvn install
重新启动main方法即可
3、CRUD
现在我们根据ID去查一个user 这时候 我们只需要改三个地方:
1 UserMapper.java
2 UserMapper.xml
3 UserMapperTest.java
3.1、Select 查询
首先 我们修改UserMapper.java
文件
在上面添加上一个方法:
// 根据ID查询用户
User getUserById(int id);
然后我们再修改UserMapper.xml
文件,在<mapper></mapper>
中补充:
<select id="getUserById" resultType="com.april.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id}
</select>
-
namespace的包名要和接口中的包名一致!
-
id:就是对应的namespace中的方法
-
resultType:就是对应namespace中 方法的返回值/Sql语句执行的返回值
-
parameterType:参数类型
然后再在userMapperTest.java
文件中 加入如下代码
@Test
public void testGetUserById(){
// 获得SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
3.2、Insert 添加
同理 我们可以写出如下语句:
-
UserMapper.java
中加上如下方法:
// 插入一个用户
int addUser(User user);
-
UserMapper.xml
的<mapper></mapper>
中补充如下代码:
<insert id="addUser" parameterType="com.april.pojo.User">
insert into mybatis.user (id,name,pwd,birthday) values (#{id},#{name},#{pwd},#{birthday})
</insert>
-
然后再在
userMapperTest.java
文件中 加入如下代码:
// 增删改需要提交事务
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.addUser(new User(4,"赵柳","456789","1949-10-01"));
if (rs>0){
System.out.println("插入成功");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
3.3、Update 修改
-
UserMapper.java
中加上如下方法:
// 修改用户名字
int updateUser(User user);
-
UserMapper.xml
的<mapper></mapper>
中补充如下代码:
<update id="updateUser" parameterType="com.april.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd},birthday=#{birthday} where id = #{id}
</update>
-
然后再在
userMapperTest.java
文件中 加入如下代码:
@Test
public void testUpdateUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.updateUser(new User(4,"朱毅","955487","1959-10-01"));
if (rs>0){
System.out.println("修改成功");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
3.4、Delete 删除
-
UserMapper.java
中加上如下方法:
// 删除用户
int deleteUser(int id);
-
UserMapper.xml
的<mapper></mapper>
中补充如下代码:
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
</delete>
-
然后再在
userMapperTest.java
文件中 加入如下代码:
@Test
public void testDeleteUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.deleteUser(4);
if (rs>0){
System.out.println("删除成功!");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
这样,一个简单的增删改查就全部完成啦
下面附上完整版源代码:
-
MybatisUtil.java
package com.april.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;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取resource目录下的mybatis-config.xml文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
-
User.java
package com.april.pojo;
public class User {
private int id;
private String name;
private String pwd;
private String birthday;
public User() {
}
public User(int id, String name, String pwd, String birthday) {
this.id = id;
this.name = name;
this.pwd = pwd;
this.birthday = birthday;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPwd() {
return pwd;
}
public String getBirthday() {
return birthday;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
", birthday='" + birthday + '\'' +
'}';
}
}
-
UserMapper.java
package com.april.mapper;
import com.april.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询全部用户
List<User> getUserList();
// 根据ID查询用户
User getUserById(int id);
// 插入一个用户
int addUser(User user);
// 修改用户名字
int updateUser(User user);
// 删除用户
int deleteUser(int id);
}
-
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:命名空间 绑定一个对应的mapper接口-->
<mapper namespace="com.april.mapper.UserMapper">
<!--id里面的内容对应你UserMapper的方法名 相当于重写方法 resultType为你调用的类名-->
<select id="getUserList" resultType="com.april.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" resultType="com.april.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id}
</select>
<insert id="addUser" parameterType="com.april.pojo.User">
insert into mybatis.user (id,name,pwd,birthday) values (#{id},#{name},#{pwd},#{birthday})
</insert>
<update id="updateUser" parameterType="com.april.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd},birthday=#{birthday} where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id}
</delete>
</mapper>
-
UserMapperTest.java
package com.april.mapper;
import com.april.pojo.User;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
// 获得SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
// 执行SQL 从sqlSession中获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 现在就可以直接从mapper中调用UserMapper的方法了 由于我们的xml文件的id直接绑定的是UserMapper的方法 所以我们直接调用内部的方法即可
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}finally {
// 关闭sqlSession
sqlSession.close();
}
}
@Test
public void testGetUserById(){
// 获得SqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
// 增删改需要提交事务
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.addUser(new User(4,"赵柳","456789","1949-10-01"));
if (rs>0){
System.out.println("插入成功");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
@Test
public void testUpdateUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.updateUser(new User(4,"朱毅","955487","1959-10-01"));
if (rs>0){
System.out.println("修改成功");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
@Test
public void testDeleteUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int rs = mapper.deleteUser(4);
if (rs>0){
System.out.println("删除成功!");
sqlSession.commit();
}
System.out.println(rs);
sqlSession.close();
}
}
3.5、错误分析
-
标签匹配错误[UserMapper.xml的insert标签错写成select]
-
mybatis-config.xml的mapper resource 使用的是点而不是斜杠
-
程序配置文件符合复合规范
-
MybatisUtil类中,已经定义了一个私有的sqlSessionFactory,但还在静态代码块中使用一个新的
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
-
输出的xml文件存在中文乱码问题[格式为GBK或者ASNI格式等]
-
maven没有导出问题
4、Map和模糊查询扩展
4.1、使用场景
当你使用update的时候 例如说,我们数据库中有100个字段,而我们只想要修改一个或者几个字段的时候 我们就没办法在UserMapper的parameterType中使用User类 这时候我们就可以用Map,又或者是我们需要查询多条数据的时候(虽然不是很正规,但是是一个比较万能的方式)
4.2、具体代码
代码如下:
UserMapper.java内容如下:
// 使用Map添加数据
// 好处:我可以不用知道这个map中有什么
int addUserForMap(Map<String,Object> map);
UserMapper.xml内容如下:
<!--map类型直接在parameterType写map即可 后面的#{id}之类的,可以和数据库的不一致,为了方便理解 我在这里全部加上user-->
<insert id="addUserForMap" parameterType="map">
insert into mybatis.user (id,name,pwd,birthday) values (#{userid},#{username},#{userpwd},#{userbirthday})
</insert>
UserMapperTest如下:
@Test
public void testAddUserForMap(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userid",4);
map.put("username","王若");
map.put("userpwd","ayjsihdu");
map.put("userbirthday","1999-01-01");
mapper.addUserForMap(map);
sqlSession.close();
}
4.3、注意事项
Map传递参数,直接在sql中取出我们的key即可!【parameterType="map"】
对象传递参数,直接在sql中取对象的属性即可!【parameterType="Object"】
只有一个基本类型参数的情况下,可以直接在sql中取到! 举例如下:
<select id="getUserById" resultType="com.april.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id}
</select>
<!--可以直接变成如下这个样子:-->
<select id="getUserById" resultType="com.april.pojo.User">
select * from mybatis.user where id = #{id}
</select>
多个参数用Map,或者注解!
4.4、模糊查询
模糊查询的方式:
UserMapper.java:
// 使用模糊查询查询用户
List<User> getUserLike(String value);
UserMapper.xml:
<select id="getUserLike" resultType="com.april.pojo.User">
select * from mybatis.user where name like concat('%',#{value},'%')
</select>
UserMappperTest.java:
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserLike("王");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
5、Mybatis配置解析
前言
以下文字部分来源于官方文档 地址:[https://mybatis.org/mybatis-3/zh/configuration.html]
5.1、核心配置文件
5.1.1、核心配置文件
-
mybatis-config.xml(名字可能不一样,官方建议使用此名字)
-
官方说明:MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
官方文档解释如下:
5.1.2、configuration顺序
这是在我们的xml文件中<configuration>
标签中,各种子标签的顺序,如果没有则跳过
properties > settings > typeAliases > typeHandlers > objectFactory > objectWrapperFactory > reflectorFactory > plugins > environments > databaseIdProvider > mappers
5.2、环境配置(environments)
环境配置
-
MyBatis 可以配置成适应多种环境
-
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
-
一般只拥有两个环境:测试环境,正式环境
我们写一个测试的mybatis-config.xml(这里将默认环境修改为了test):
<?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:核心配置文件-->
<configuration>
<!--default默认环境-->
<environments default="test">
<environment id="development">
<!--transactionManager:事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<!--id为你这个环境的名字(在上面的default="development"进行修改,将development换为你要切换的环境即可)-->
<environment id="test">
<!--transactionManager:事务管理器-->
<transactionManager type="JDBC"/>
<!--dataSource:数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
</configuration>
注意一些关键点:
-
默认使用的环境 ID(比如:default="development")。
-
每个 environment 元素定义的环境 ID(比如:id="development")。
-
事务管理器的配置(比如:type="JDBC")。
-
数据源的配置(比如:type="POOLED")。
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
-
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
-
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
-
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。 -
url
– 这是数据库的 JDBC URL 地址。 -
username
– 登录数据库的用户名。 -
password
– 登录数据库的密码。 -
defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。 -
defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
-
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8
的 encoding
属性给数据库驱动。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
-
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10 -
poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。 -
poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -
poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。 -
poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections
与poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5) -
poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。 -
poolPingEnabled
– 是否启用侦测查询。若开启,需要设置poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。 -
poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
总结
UNPOOLED与POOLED的差别:
是否有池的概念,池:用完回收等下一个人连接数据库的时候继续使用,不会将其进行注销;
并且要学会使用配置多套运行环境(上面有代码)[测试的时候可以换着来,但是最终只能实现一个];
重点理解:Mybatis默认的事务管理器就是JDBC,默认的数据源是POOLED;
5.3、属性(properties)
前言
我这里使用了第三章CRUD的代码进行测试,在这个之前你们可以把整个第三章的代码进行复制出来
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
编写一个配置文件(db.propertites):
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
username=root
password=123456
在核心配置文件(mybatis-config.xml)中引入[按照我最上面的configuration优先级进行顺序的编写]:
<?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:核心配置文件-->
<configuration>
<!--
由于我们是在同级目录 所以我可以直接写db.properties 如果不在同级目录则需要写上全路径(用/隔开)
我们可以不用写里面的`property name="username"`之类的 我们直接用下面的方法来引入配置文件就可以
-->
<properties resource="db.properties"/>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<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都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
</configuration>
使用官方文档的读取的话,我们可以去掉我们的username和password:
db.propertites代码如下:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
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:核心配置文件-->
<configuration>
<!--
由于我们是在同级目录 所以我可以直接写db.properties 如果不在同级目录则需要写上全路径(用/隔开)
-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<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都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
</configuration>
结果:上面的两种方法都可以跑出来
优先级测试
db.propertites代码如下:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
username=root
password=123456
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:核心配置文件-->
<configuration>
<!--
由于我们是在同级目录 所以我可以直接写db.properties 如果不在同级目录则需要写上全路径(用/隔开)
-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="654321"/>
</properties>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<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都需要在Mybatis核心配置文件中注册-->
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
</configuration>
如果运行测试失败,则证明xml中的property属性优先级要高一点
测试结果为正确跑出了数据,则证明直接调用外部配置文件的优先级会比直接在xml中写死的优先级要高(首先读取在 mybatis-config.xml中的properties 元素体内指定的属性,最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。)
总结
-
可以直接引入外部文件
-
可以在其中增加一些属性配置
-
如果两个文件有同一个字段,则会先读取配置方法中的字段,再读取配置文件的字段,如果有同名的属性会通过配置文件的属性进行覆盖
5.4、类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。这样在我们的UserMapper.xml中,原本是这么写的:
<select id="getUserList" resultType="com.april.pojo.User">
select * from mybatis.user
</select>
如果我们给类型别名配置完成之后,就可以这么写:
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
代码如下
第一种方式
typeAlias:为一个具体的类设置单独的一个别名
mybatis-config.xml(里面的alias和type根据你实际的包名和类名进行切换):
<!--typeAliases类型别名-->
<typeAliases>
<!--这其中有两个使用方式,第一种是package,第二种是typeAlias,我们先使用typeAlias进行测试-->
<typeAlias type="com.april.pojo.User" alias="User"></typeAlias>
</typeAliases>
UserMapper.xml:
<!--将<select id="getUserList" resultType="com.april.pojo.User">改为<select id="getUserList" resultType="User">-->
<select id="getUserList" resultType="User">
select * from mybatis.user
</select>
第二种方式
package:MyBatis 会在包名下面搜索需要的 Java Bean(扫描实体类的包),它的默认别名就为这个类的类名,首字母建议小写!(优点是不用配置多个类)
mybatis-config.xml:
<typeAliases>
<package name="com.april.pojo"/>
</typeAliases>
UserMapper.xml:
<select id="getUserList" resultType="user">
select * from mybatis.user
</select>
具体使用情况
在实体类比较少的时候,使用第一种方式
如果实体类十分多,建议使用方式
优缺点:第一种可以DIY别名,第二种DIY别名有点麻烦,需要使用注解,详情如下:
每一个使用 package的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
@Alias("hello")
public class User {
...
}
这时候我们将resultType="user"
改为resultType="hello"
就可以正常运行了(如果还是报错则检查是否所有user都改为hello了)
<select id="getUserList" resultType="user">
select * from mybatis.user
</select>
常见的 Java 类型内建的类型别名
下面是一些为常见的 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 |
5.5、设置(settings)
这是 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。(假设数据库列名是last_name,如果开启了这个规则则会自动转为为lastName)[数据库使用_ 的原因是因为之前使用的是oracle数据库,他会自动把所有字段转成大写,所以在那时候就已经有些惯性思维,数据库中的字段都用下划线进行分割] |
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(deprecated since 3.5.9) | 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 | 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) | true | false | false |
defaultSqlProviderType | Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type (or value ) attribute on sql provider annotation(e.g. @SelectProvider ), when these attribute was omitted. |
A type alias or fully qualified class name | Not set |
nullableOnForEach | Specifies the default value of 'nullable' attribute on 'foreach' tag. (Since 3.5.9) | true | false | false |
5.6、其他配置
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂
-
plugins(插件)
-
mybatis-generator-core(是一款可以自动生成mybatis代码的文件)
-
mybatis-plus(mybatis的第三方插件,使用可以让mybatis的效率更高)
-
通用mapper
-
5.7、映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件(在mapper元素解析的时候 我们会用到这个类)
方式一【推荐使用】
<mappers>
<mapper resource="com/april/mapper/UserMapper.xml"/>
</mappers>
这个是目前来说,我们最常用的方式,现在介绍一下其他的几种方式:
方式二 使用class文件绑定注册
<mappers>
<mapper class="com.april.mapper.UserMapper"/>
</mappers>
使用mapper class
的注意点:
-
接口和它的Mapper配置必须同名
-
接口和它的Mapper配置必须在同一个包下
方式三 使用扫描包进行注入绑定
<mappers>
<package name="com.april.mapper"/>
</mappers>
-
这个方式和我们类型别名的package其实是差不多的,都是直接扫描这个下面的所有包,从而进行注册
-
但是:这个接口和它的Mapper配置也是必须同名的 不然会出现(not found)的这个错误
-
如果放到resource目录下的话 在resource目录下同包 同名 则也是没有问题的 如果直接放在resource目录下则会报错
5.8、生命周期和作用域
生命周期,和作用域都是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
-
一旦创建了SqlSessionFactory,就不再需要它了
-
局部变量
SqlSessionFactory:
-
说白了就是理解为/想象为数据库连接池
-
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
-
因此 SqlSessionFactory 的最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式。
-
也就是说 SqlSessionFactory为一个全局变量,程序开始的时候即为开始,程序结束的时候即为结束
SqlSession:
-
连接到连接池的一个请求
-
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。(放到方法中即可)
-
用完之后需要赶紧关闭,否则资源将被占用!
-
-
这里面的每一个Mapper,就代表一个具体的业务(insert update delete select)
6、ResultMap
6.1、问题介绍
数据库中的字段,我们现在是这样的:
现在我们新建一个项目,拷贝之前的,测试实体类字段不一致的情况(记得重写构造方法)
将pwd的字段名改为password,然后根据ID查询数据
然后发现 这里的password为null
原因:
我们UserMapper.xml中是这么写的:select * from mybatis.user where id = #{id}
但程序中实际是这么认为的:select id,name,pwd,birthday from mybatis.user where id = #{id}
按照逻辑 我们获取到的是数据库的password字段,而并非pwd字段
6.2、解决方法
第一种(最简单粗暴,也是最笨的解决方式) :起别名
select id,name,pwd as password,birthday from mybatis.user where id = #{id}
第二种 使用resultMap
1 先将UserMapper.xml中的sql改回来
2 将UserMapper.xml中的resulttype改为resultMap(通过结果集映射)
数据库中的样子 | id | pwd | name | birthday |
---|---|---|---|---|
java代码中的样子 | id | password | name | birthday |
结果如图:我们原本调用的是一个resultType,现在我们改成了resultMap,并在mapper标签中定义了一个resultMap,说简单一点也就是将数据库的列,通过这个映射到属性库中的字段中
-
resultMap
元素是 MyBatis 中最重要最强大的元素。 -
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
当然,上述方式只是简单的将所有的列映射到HashMap的键上,这个会由我们的resultType属性指定。虽然在大部分情况下完全够用,但是 HashMap 并不是一个很好的领域模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为领域模型。MyBatis 对两者都提供了支持。
7、日志
7.1、日志工厂
如果一个数据库操作,出现了异常,我们需要排查错误,日志就是最好的助手
曾经:System.out.println();,debug
现在:日志工厂!
-
SLF4J
-
LOG4J(deprecated since 3.5.9)
-
LOG4J2(log4j的升级版)
-
JDK_LOGGING (JAVA自带日志输出)
-
COMMONS_LOGGING (工具包输出)
-
STDOUT_LOGGING(控制台输出)
-
NO_LOGGING(无日志输出)
在Mybatis中,具体使用哪一个日志实现,在设置中设定!
日志配置(STDOUT)
这就是STDOUT的输出结果:
Opening JDBC Connection // 打开JDBC
Created connection 2143431083. //创建连接对象
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7fc229ab]
==> Preparing: select * from mybatis.user where id = ? // SQL语句
==> Parameters: 1(Integer) // 参数
<== Columns: id, name, pwd, birthday //列
<== Row: 1, 张三, zhangsan, 2008-01-01 //列对象
<== Total: 1
User{id=1, name='张三', password='zhangsan', birthday='2008-01-01'} // 查询结果
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7fc229ab]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7fc229ab] //关闭JDBC
Returned connection 2143431083 to pool.
7.2、Log4j
什么是Log4j
-
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件,或者GUI组件
-
我们也可以控制每一条日志的输出格式
-
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
-
这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
7.2.1、 导入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
7.2.2、 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=【%c】-%m%n
# 文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
# 文件设置的地址
log4j.appender.file.File=./log/april.log
# 日志的最大文件 如果超过10mb则生成一个新的文件
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{yyyy-MM-dd hh:mm:ss}][%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
7.2.3、 配置log4j为日志的实现(空格不要多,大小写不要错)
<settings>
<!--标准的日志工厂实现-->
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
<setting name="logImpl" value="LOG4J"/>
</settings>
7.2.4、 Log4j的使用
简单使用
我们在测试类中再写一个test
1 在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;
2 日志对象,加载参数为当前类的class
3 如图,这样进行打印即可
8、分页
分页的原因:减少数据的处理量
8.1、使用Mybatis实现Limit分页
SQL语法
select * from user limit startindex,pageSize
-- 每一页显示两个 从第0个开始查
select * from user limit 0,2;
-- 查询前2条记录
select * from user limit 2; #[0,n]
1 接口代码
// 分页实现查询
List<User> getUserByLimit(Map<String,Integer> map);
2 Mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{startIndex},#{pageSize}
</select>
3 测试类
@Test
public void testLimit(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
8.2、分页插件
前言
在你的项目没有足够大的时候,不建议使用分页插件;
使用方法:https://pagehelper.github.io/docs/howtouse/
了解即可,万一以后公司需要使用,你需要知道他是什么东西
9、使用注解开发
9.1、面向接口编程
三个面向区别
-
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
-
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
-
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;
9.2、使用注解进行实际开发
9.2.1、实际应用
将上一个项目复制过来,并只留下这些文件,然后将日志输出改为标准的日志工厂模式,再将mappers这个标签给去除掉,重新创建一个mapper文件夹,里面包含一个UserMapper的接口
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:核心配置文件-->
<configuration>
<!--
由于我们是在同级目录 所以我可以直接写db.properties 如果不在同级目录则需要写上全路径(用/隔开)
-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<settings>
<!--标准的日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--typeAliases类型别名-->
<typeAliases>
<!--这其中有两个使用方式,第一种是package,第二种是typeAlias,我们先使用typeAlias进行测试-->
<!-- <typeAlias type="com.april.pojo.User" alias="User"></typeAlias>-->
<package name="com.april.pojo"/>
</typeAliases>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<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 class="com.april.mapper.UserMapper"/>
</mappers>
</configuration>
UserMapper.java
package com.april.mapper;
import com.april.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
UserMapperTest.java
package com.april.mapper;
import com.april.pojo.User;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
}
执行结果如下:
我们可以看到,我们之前修改的password,现在仍然为空,因为我们的注解只适用于一些简单的sql,复杂一点的sql的话我们还是需要使用xml进行配置与使用
本质:反射机制实现
底层:动态代理!
9.2.2、Mybatis详细的执行流程
9.3、注解增删改查
我们可以在工具类创建的时候自动提交事务!只需要在MybatisUtil里面修改如下代码(注释为之前的代码):
但是!一般不要设置自动提交,如果自己代码出了问题导致数据库出现问题,不自动提交也方便回滚,我这里测试的话 还是加上自动注入,平常使用的时候注意一点即可
完整增删改查代码如下:
MybatisUtil.java:
package com.april.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;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取resource目录下的mybatis-config.xml文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
// public static SqlSession getSqlSession(){
// return sqlSessionFactory.openSession();
// }
}
UserMapper.java:
package com.april.mapper;
import com.april.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
public interface UserMapper {
// 查询所有User
@Select("select * from user")
List<User> getUsers();
// 根据ID查询 基本类型使用注解的时候 都建议加上@Param
// 方法存在多个参数的话,所有的参数前面必须加上@Param注解
@Select("select * from user where id = #{id} and name = #{name}")
User getUserByParam(@Param("id") int id,@Param("name") String name);
// 引用对象无需写@param #{}里面应用的参数名要和实体类中一致
@Insert("insert into user(id,name,pwd,birthday) values (#{id},#{name},#{password},#{birthday})")
int addUser(User user);
@Update("update user set name=#{name},pwd=#{password},birthday=#{birthday} where id = #{id}")
int updateUser(User user);
// 这里将param改为uid,是为了理解,注解和mapper.xml中,调用的#{},里面都是使用的@param中的参数名,如果没有填写,才使用方法中的参数名
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
}
UserMapperTest.java:
package com.april.mapper;
import com.april.pojo.User;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void testSelectByParam(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User res = mapper.getUserByParam(1, "张三");
System.out.println(res);
sqlSession.close();
}
@Test
public void testInsertUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 因为我们在配置之后,我们可以无需手动提交,而改为自动提交了
mapper.addUser(new User(5,"爪巴","10086","2022-01-01"));
sqlSession.close();
}
@Test
public void testUpdateUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(5,"四月一","10086","2022-01-01"));
sqlSession.close();
}
@Test
public void testDeleteUser(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(5);
sqlSession.close();
}
}
10、Lombok
10.1、使用步骤
1 在IDEA中安装Lombok插件
2 在项目中导入lombok的jar包(从maven仓库下载下来的记得删掉scope)
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
3 在实体类上加上注解即可!
@Getter and @Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@Data
==== 以上是暂时需要掌握的 ====
==== 以下是Lombok带的其他方法 ====
@FieldNameConstants
@RequiredArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
@Data:生成了无参构造、get、set、toString、hashCode、equals方法
@AllArgsConstructor:生成了有参构造的方法
@NoArgsConstructor:生成了无参构造的方法
package com.april.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// data这个注解自带get/set方法
@Data
// 有参构造
@AllArgsConstructor
// 无参构造
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
private String birthday;
}
10.2、Lombok的优缺点
优点
-
能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
-
让代码变的简洁,不用过多的去关注相应的方法
-
属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等
缺点
-
不支持多重参数构造器的重载
-
虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度
10.3、总结
Lombok虽然有很多优点,但是Lombok更类似于一种IDE插件,项目也需要依赖相应的jar包,Lombok依赖jar包是因为编译的时候需要用他的注解,说他类似插件的原因是因为在使用的时候,eclipse和IDEA都需要安装相应的插件,在编辑器编译的时候通过操作AST(抽象语法树)改变字节码生成,变向的就是说在改变java语法。
它不像spring的依赖注入或者mybatis的ORM一样是运行时的特性,而是编译时的特性。因为Lombok主要是靠的对插件的依赖,而且只是省去了一些人工生成代码的麻烦,但现在的IDE都有快捷键来协助生成我们的getter/setter等方法,也非常方便。
知乎上有位大神发表过对L ombok的一些看法: 这是一种低级趣味的插件,不建议使用。 JAVA发展到今天, 各种插件层出不穷,如何甄别各种插件的优劣?能从架构上优化你的设计的,能提高应用程序性能的,实现高度封装可扩展的....像Lombok这种, 像这种插件,已经不仅仅是插件了,改变了你如何编写源码,事实上,少去了的代码,写上去又如何?如果JAVA家族到处充斥这样的东西, 那只不过是一坨披着金属颜色的屎,迟早会被其它的语言取代。
虽然话糙但理确实不糙,试想一个项目有非常多类似Lombok这样的插件,个人觉得真的会极大的降低阅读源代码的舒适度。虽然非常不建议在属性的getter/setter写一些业务代码, 但在多年项目的实战中,有时通过给getter/setter加一 点点业务代码,能极大的简化某些业务场景的代码。所谓取舍,也许就是这时的舍弃一定的规范,取得极大的方便。
我现在非常坚信一条理念, 任何编程语言或插件,都仅仅只是工具而已,即使工具再强大也在于用的人,就如同小米加步枪照样能赢飞机大炮的道理一样。结合具体业务场景和项目实际情况,无需一 味追求高大上的技术,适合的才是王道。
Lombok有它的得天独厚的优点,也有它避之不及的缺点,熟知其优缺点,在实战中灵活运用才是王道。
11、多对一处理
11.1、讲解/准备
假设左边是学生 右边是老师
-
多个学生 对应一个老师
-
对于学生这边而言,我们用关联来代表这里的关系,也就是说,多个学生只有一个老师叫关联【多对一】
-
对于老师这边而言,我们用集合来代表这里的关系,也就是说,一个老师有很多个学生叫集合【一对多】
SQL
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');
这时候我们可以看到,学生表中有5个学生,他们关联着同一个老师,老师表里只有一个老师,但它关联着五个学生
我们新建一个项目:
将MybatisTest5中的resources下面的db.properties
和mybatis-config.xml
拷贝过来,再将mybatis-config.xml
中的mappers删除,这样我们就获得了一个干净的项目了;
11.2、测试环境搭建
1 新建Teacher,Student表的实体类
-
Teacher.java:
package com.april.pojo;
public class Teacher {
private Integer id;
private String name;
public Teacher() {
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
-
Student.java
package com.april.pojo;
public class Student {
private Integer id;
private String name;
// 学生需要关联一个老师
private Teacher teacher;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
2 建立Mapper接口
-
TeacherMapper.java
package com.april.mapper;
import com.april.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface TeacherMapper {
@Select("select * from teacher where id = #{tid}")
Teacher getTeacher(@Param("tid") int id);
}
-
StudentMapper.java(因为没做查询 所以暂时没有数据)
package com.april.mapper;
public interface StudentMapper {
}
3 建立Mapper.xml文件(放到resources目录下的com/april/mapper文件夹下【和java目录下同级】)
请注意:这里的头部和我们的mybatis-config.xml不一致,请注意区分,导入的是mapper的dtd约束文件
-
TeacherMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.TeacherMapper">
</mapper>
-
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.StudentMapper">
</mapper>
4 在mybatis-config.xml中注册我们的Mapper【可以用mapper resource,也可以用mapper class,根据自己喜好进行选择】
<?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:核心配置文件-->
<configuration>
<!--
由于我们是在同级目录 所以我可以直接写db.properties 如果不在同级目录则需要写上全路径(用/隔开)
-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<settings>
<!--标准的日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--typeAliases类型别名-->
<typeAliases>
<!--这其中有两个使用方式,第一种是package,第二种是typeAlias,我们先使用typeAlias进行测试-->
<!-- <typeAlias type="com.april.pojo.User" alias="User"></typeAlias>-->
<package name="com.april.pojo"/>
</typeAliases>
<!--default默认环境-->
<environments default="development">
<environment id="development">
<!--transactionManager:事务管理-->
<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="com/april/mapper/StudentMapper.xml"></mapper>
<mapper resource="com/april/mapper/TeacherMapper.xml"></mapper>
</mappers>
</configuration>
5 测试查询
import com.april.mapper.TeacherMapper;
import com.april.pojo.Teacher;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
public class MyTest1 {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
}
11.3、嵌套处理
我们现在需要查询学生表,并将老师表中老师的数据也一并查询出来,数据库语句类似于如下的效果
select s.id,s.name,t.namefrom student s,teacher t where s.tid = t.id
11.3.1、按照查询嵌套处理
测试查询还是一样的代码
import com.april.mapper.TeacherMapper;
import com.april.pojo.Teacher;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
public class MyTest1 {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
}
StudentMapper.xml的代码需要改一下(仅适用于数据少的情况):
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.StudentMapper">
<!--
思路:
1. 查询所有学生信息
2. 根据查询出来的学生tid,寻找对应的老师
-->
<!-- 这时候我们发现 直接用下列语句 会出现teacher=null的情况 -->
<!-- <select id="getStudent" resultType="Student">-->
<!-- select * from student-->
<!-- </select>-->
<select id="getStudent" resultMap="StudentAndTeacher">
select * from student
</select>
<!-- id为我们上面定义的resultMap Type为我们getStudent这个方法的返回值-->
<resultMap id="StudentAndTeacher" type="Student">
<!--学生表中的ID-->
<result property="id" column="id"></result>
<!--学生表中的name-->
<result property="name" column="name"></result>
<!--
由于我们Student实体类中写了一个Teacher的对象,这个对象中有多个字段,我们将它理解为一个复杂的属性
复杂的属性,我们需要单独的进行处理 其中对象的处理方式为:association 集合的处理方式为:collection
这里的property="teacher"对应了我们Student实体类中 private Teacher teacher; 的teacher
column="tid" 是调用了getTeacher中的参数 因为tid是外键,会自动关联到id
所以这里填id是可以的, 为了方便查看你也可以将下面的#{id}改为#{tid}
javaType="Teacher" 是我们java中进行的返回数据类型 private Teacher teacher; 这里的数据类型是Teacher
select="getTeacher" 就是调用了下方getTeacher的这个select标签
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id = #{id}
</select>
</mapper>
11.3.2、按照结果嵌套处理
StudentMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.StudentMapper">
<select id="getStudent" resultMap="StudentAndTeacher">
select s.id as sid,s.name as sname,t.id as tid,t.name as tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentAndTeacher" type="Student">
<!--由于我们之前没有取别名 所以result中的column都是取的数据库本来的名字,我们现在取了别名之后 对应的列就要为别名的名称-->
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<!--
property="teacher" 还是对应了我们Student实体类中 private Teacher teacher; 的teacher
javaType="Teacher" 也还是我们java中进行的返回数据类型 private Teacher teacher; 这里的数据类型是Teacher
在里面添加result是因为我们对他Teacher的这个对象 进行了属性的赋值
id 就是我们查询语句中的tid name就是我们查询语句中的tname
-->
<association property="teacher" javaType="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
</association>
</resultMap>
</mapper>
测试查询还是一样的代码
import com.april.mapper.TeacherMapper;
import com.april.pojo.Teacher;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
public class MyTest1 {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
}
回顾Mysql多对一查询方式
-
子查询
-
连表查询
12、一对多处理
比如:一个老师拥有多个学生!对于老师而言,就是一对多的关系
12.1、测试环境搭建
新建一个项目,将多对一写的代码清空,并修改实体类代码:
Student.java
package com.april.pojo;
public class Student {
private Integer id;
private String name;
private int tid;
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", tid=" + tid +
'}';
}
}
Teacher.java
package com.april.pojo;
import java.util.List;
public class Teacher {
private Integer id;
private String name;
private List<Student> students;
public Teacher() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", students=" + students +
'}';
}
}
由于我这里使用的是一对多,也就是一个老师对应多个学生,那么我们将原本属于学生的List<Teacher>
改为属于老师的List<Student>
;
12.2、嵌套处理
12.2.1、按照查询嵌套处理
TeacherMapper.xml(第一种getTeacher是student为null的错误示范):
<?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">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.TeacherMapper">
<select id="getTeacher" resultType="Teacher">
select * from teacher
</select>
<select id="getTeacherAndStudent" resultMap="TeacherAndStudent">
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 = #{tid}
</select>
<resultMap id="TeacherAndStudent" type="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<!--
由于我们的pojo实体类中使用的是集合,所以这里使用collection property用的就是实体类中的属性名
javaType:指定属性的类型
集合中的泛型信息,我们要使用ofType获取,实际上并无太大区别,只不过集合必须要用ofType
但是在collection里面写的内容和association是一致的
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
</mapper>
TeacherMapper.java:
package com.april.mapper;
import com.april.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TeacherMapper {
// 获取老师
List<Teacher> getTeacher();
// 获取指定老师下的所有学生及老师的信息
Teacher getTeacherAndStudent(@Param("tid") int id);
}
Test:
import com.april.mapper.TeacherMapper;
import com.april.pojo.Teacher;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest1 {
@Test
public void Test1(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teacherList = mapper.getTeacher();
for (Teacher teacher : teacherList) {
System.out.println(teacher);
}
sqlSession.close();
}
@Test
public void Test2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacherAndStudent(1);
System.out.println(teacher);
sqlSession.close();
}
}
12.2.2、按照结果嵌套处理
TeacherMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace="com.april.mapper.TeacherMapper">
<!-- 查询老师表 -->
<select id="getTeacherAndStudent" resultMap="TeacherAndStudent">
select * from teacher where id = #{tid}
</select>
<!-- 根据老师的ID去查询学生 -->
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{tid}
</select>
<!-- 返回的结果
collection:代表集合
property:代表Teacher中的列
javaType:指定pojo中属性的类型
ofType:映射到list集合属性中pojo的类型
select:调用嵌套的标签id
column:resultMap中返回的Type中类型的字段
-->
<resultMap id="TeacherAndStudent" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="tid"></collection>
</resultMap>
</mapper>
TeacherMapper.java:
package com.april.mapper;
import com.april.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
public interface TeacherMapper {
// 获取指定老师下的所有学生及老师的信息
Teacher getTeacherAndStudent(@Param("tid") int id);
}
MyTest.java:
import com.april.mapper.TeacherMapper;
import com.april.pojo.Teacher;
import com.april.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class MyTest1 {
12.3、小结
-
关联 - association 【多对一】
-
集合 - collection 【一对多】
-
javaType&ofType
-
javaType 用来指定实体类中属性的类型
-
ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
-
注意点
-
保证SQL的可读性,尽量保证通俗易懂
-
注意一对多和多对一中,属性名和字段的问题
-
如果不好排查错误 可以使用日志来进行解决
慢SQL
例如说,你和A需要查询出同一段数据,而A写出来的SQL语句进行查询的时候只需要1秒,而你查询的时候需要100秒甚至更多
因为我们现在写代码只是查询出一点点数据,如果数据像正式开发环境一样,数据开始变多,就会存在这样的情况,我们这个SQL没加任何优化,对于公司来说就是有问题的代码,所以自己写SQL的风险还是比较高,我们可以学习到一些思路之后,把一些东西总结到自己的文档/博客等里面;可以把这些代码作为一个参考,来写你们工作中的代码
-
SQL高频面试题
-
Mysql引擎(InnoDB和MyISAM)
-
InnoDB底层原理
-
索引,索引优化
-
13、动态SQL
13.1、什么是动态SQL
什么是动态SQL
动态SQL指的是根据不同的条件来生成不同的SQL语句
官方解释:
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
-
if
-
choose (when, otherwise)
-
trim (where, set)
-
foreach
13.2、搭建环境
1 创建博客表
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
2 创建一个基础工程
-
导包
新建一个MybatisTest8的java项目
-
编写配置文件
将resources的
db.properties
和mybatis-config.xml
,以及utils下面的MybatisUtils.java
拷贝过来(mybatis-config.xml中的mappers标签里面记得修改一下) -
编写实体类(记得加上get/set方法)
package com.april.pojo;
import java.util.Date;
public class Blog {
// 用户ID
private String id;
// 标题
private String title;
// 作者
private String author;
// 创建时间
private Date createTime; // sql中为create_time
// 浏览量
private Integer views;
}
-
编写实体类对应的Mapper接口和Mapper.xml
BlogMapper.java[在java目录的com.april.mapper下]:
package com.april.mapper;
public interface BlogMapper {
}
BlogMapper.xml[在resources目录的com.april.mapper下]:
-
添加一个获取uuid的工具类
package com.april.utils;
import org.junit.Test;
import java.util.UUID;
public class IDUtils {
/**
* @Author 四月一
* @return UUID
* @Description 获得一个UUID
* 因为部分企业中有这样一个应用,他们的ID不是那种123456789这种,他们是一个随机的UUID,为了保证唯一和随机
*/
public static String getID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
由于我们pojo类中的时间名字为createTime,而数据库中的为create_time,这种带下划线的我们在java中要改为驼峰命名法,要解决这种驼峰命名和数据库字段不一致的问题我们只需要在mybatis-config.xml
中多添加一个设置即可;在settings的标签下添加一行:<setting name="mapUnderscoreToCamelCase" value="true"/>
现在我们的数据库中是没有数据的,所以我们创建一个添加数据的方法(为了方便,我就不加上文件名了,自行理解一下):
// 插入数据
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>
13.3、IF
官方解释:
需要实现:如果不传递参数 则直接查询所有 如果传递参数 则根据传递的参数查询
代码如下:
// 查询博客(通过IF判断)
List<Blog> queryBlogIf(Map map);
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog where true
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
13.4、trim(where,set)
13.4.1、where
我们在13.3中可以看到 我们的where默认写成了一个where true,不然我们的and会无法使用 毕竟我们的sql语句不能直接拼接and,如果直接用where true的话,我们是不好防止SQL注入的,针对这个情况,我们就可以使用where标签。
例如说,我们刚刚写的语句是这样的:
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog where true
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
现在我们可以改成这样:
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
对于这个 官方是这么解释的:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
13.4.2、set
我们如果修改数据的时候,我们set标签每set一条数据后面就要加上逗号,如果要转换成动态,我们的逗号就要自动去除,针对这个情况,我们就可以使用set标签。
演示一下代码:
// 更新博客
int updateBlog(Map map);
<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>
这里我们可以看到,我们的set标签里面是都带有逗号的,但是这些代码却能运行成功,原因是set标签会删除无关的逗号,官方说明如下:
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
13.4.3、trim
我们的trim元素相当于是set和where的集合体,都可以使用,还可以进行自定义
例如说我们的where标签是这样:
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
我们就可以修改成这样:
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</trim>
</select>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
我们的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>
我们就可以修改成这样:
<update id="updateBlog" parameterType="map">
update blog
<trim prefix="SET" suffixOverrides=",">
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</trim>
where id = #{id}
</update>
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
具体的详情可以通过下面的表来看:
属性 | 描述 |
---|---|
prefix | 给sql语句拼接的前缀 |
suffix | 给sql语句拼接的后缀 |
prefixOverrides | 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND" |
suffixOverrides | 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定 |
13.5、choose(when,otherwise)
官方解释:
需要实现:如果不传递参数 则返回符合条件的数据 如果传递参数 则根据传递的参数查询
// 查询博客(通过choose判断)
List<Blog> queryBlogChoose(Map map);
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views > 0
</otherwise>
</choose>
</where>
</select>
由于choose标签的特殊性,等同于java中的switch,所以他不管传递多少个参数,都只会返回出一个结果,具体的详情我们可以看控制台输出的日志文件(会输出SQL语句)
13.6、SQL片段
有的时候,我们可能会将一些公共的部分抽取出来,方便复用
例如说,我们刚刚的
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
这一段,我们使用了多次,我们就可以把他封装成一个sql片段,从而导入使用
<sql id="if-title-author">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<include refid="if-title-author"></include>
</trim>
</select>
像这样就可以啦!
使用方式
-
使用SQL标签抽取公共部分
-
在需要使用的地方 使用include标签引用即可
注意事项
-
最好基于单表来定义SQL片段
-
不要存在where标签
13.7、foreach
我们需要实现如下功能:
select * from user where id in (1,2,3);
代码如下:
// 查询博客(通过Foreach)
List<Blog> queryBlogForeach(Map map);
<!--
collection:集合 需要从Map里面传递
item:从这个集合中遍历出来的每一项(集合项)
index:索引
open:在开头字符串放置分隔符
close:在结尾字符串放置分隔符
separator:在foreach每循环一次之后放置分隔符
-->
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
id in
<foreach collection="list" item="id" index="index" open="(" separator="," close=")">
#{id}
</foreach>
</where>
</select>
13.8、小结
其实动态SQL就是在拼接我们的SQL语句,我们只需要保证SQL的正确性,按照SQL的方式,去排列组合就可以了
建议
-
先在mysql中写出完整的SQL,再对应的去修改成我们的动态SQL来实现通用
14、缓存
14.1、简介
-
什么是缓存?[cache]
-
存在内存中的临时数据
-
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
-
-
为什么要使用缓存?
-
减少和数据库的交互次数,减少系统开销,提高系统效率。
-
-
什么样的数据能够使用缓存?
-
经常查询并且不经常改变的数据。
-
14.2、Mybatis缓存
-
Mybatis缓存包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大的提升查询效率。
-
Mybatis系统中默认定义了两种缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也成为本地缓存)
-
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
-
为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。
-
14.3、一级缓存
介绍
我们的Sqlsession只在开启和sqlsession.close()之间有效,离开之后就无效了
-
一级缓存也叫本地缓存:
-
与数据库同一次回话期间查询到的数据会放到本地缓存中
-
如果以后需要获取相同的数据,直接从缓存中拿,没有必要重新查询数据库
-
测试步骤
-
开启日志
-
测试在一个Session中查询两次记录
-
查看日志输出
User queryUserByID(
<select id="queryUserByID" resultType="User">
select * from user where id = #{id}
</select>
这里我们可以看到 从开启到结束 我们只进行了一次SQL查询 查出了两个对象 说明了我们另外一个结果是从缓存中进行读取的;
缓存失效的情况:
-
增删改操作,可能会改变原来的数据,所以必定会刷新缓存
-
查询不同的东西
-
查询不同的Mapper
-
手动清理缓存
小结
一级缓存是默认开启的,只在一次Sqlsession中有效,也就是拿到连接到关闭连接这个区间段!
14.4、二级缓存
介绍
-
二级缓存也叫全局缓存,一级缓存作用于太低了,所以诞生了二级缓存;
-
基于namespace级别的缓存,一个命名空间,对应一个二级缓存
-
工作机制
-
一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
-
如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,以及缓存中的数据被保存到二级缓存中;
-
新的会话查询信息,就可以从二级缓存中获取内容;
-
不同的mapper查询出的数据会放在自己对应的缓存中;
-
如何开启二级缓存
-
开启全局缓存
在
mybatis-config.xml
的sesstings
标签中输入如下内容:<!--开启全局缓存-->
<setting name="cacheEnabled" value="true"/> -
在要使用二级缓存的
Mapper.xml
中添加标签<cache/>
(这个只适用于不定制任何功能 我下面的代码是带定制的 详情看注释)<!--
在当前Mapper.xml使用二级缓存
eviction: 代表清除策略
总共四个清除策略:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval:代表间隔多少秒刷新
size:代表最多存储多少个引用
readonly:为true则标识只读
-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
<!--useCache="true"代表使用二级缓存 useCache="false"代表不使用二级缓存-->
<select id="queryUserByID" resultType="User" useCache="true">
select * from user where id = #{id}
</select> -
这里我们启用缓存之后,发现不同的连接调用的也是同一个缓存了
测试可能出现的问题
pojo类的实体类记得序列化一下,不然可能会在使用的时候报错!
public class User implements Serializable {
private Integer id;
private String name;
private String pwd;
private String birthday;
}
小结
-
只要开启了二级缓存,在同一个Mapper下就有效
-
所有的数据都会先放在一级缓存中;
-
只有当会话提交/会话关闭的时候,才会提交到二级缓存中;
14.5、缓存原理
14.6、自定义缓存-ehcache
这里可以自己选择跳过,因为目前的主流缓存容器是redis,这里只做简单的使用讲解,实际使用与其他的并无太大区别
-
导包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency> -
调用(Mapper.xml)
<!--调用EhcacheCache缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/> -
配置文件(resouse目录下ehcache.xml)
由于我们缓存目前会用Redis来做缓存,而这个缓存效率比较低,所以也只是给大家教一下怎么调用自定义缓存;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)