《MyBatis》学习笔记
# 《MyBatis》学习笔记
# 一、🍊MyBatis简介🍊
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 是一款轻量级的、ORM的框架
# 二、第一个MyBatis程序💛
# 2.1 搭建环境
- 数据库 gokudb
- 所需Jar包
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.2 mybatis-config.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/goku?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="795200"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="com/singerw/mapper/UserMapper.xml"/>
</mappers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.3 编写MyBatisUtils工具类
/**
* @Author: CodeSleep
* @Date: 2021/8/23 21:15
* @Description: //TODO MyBatis工具类
*/
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
// 加载资源
static {
try {
// 使用MyBatis获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @return
* @Author CodeSleep
* @Date: 2021/8/23 21:13
* @Description: //TODO 创建执行SQL的对象
* sqlSessionFactory中获取SqlSession实例
* SqlSession完全包含了面向数据库执行SQL命令所需的所有方法;
*/
public static SqlSession getSqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 2.4 编写代码
- 实体类(
User.java
)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int userid;
private String username;
private String userphone;
private String userpassword;
private int jurisdiction;
private String createtime;
private String logintime;
private int userstatus;
}
2
3
4
5
6
7
8
9
10
11
12
13
- Dao数据持久层接口(
UserMapper.java
)
public interface UserMapper {
List<User> getUsers(int userid);
}
2
3
4
- Dao数据持久层(
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">
<mapper namespace="com.singerw.mapper.UserMapper">
<select id="getUsers" resultType="com.singerw.pojo.User">
select * from goku.g_users where userid = #{userid}
</select>
</mapper>
2
3
4
5
6
7
8
9
- 测试类(
UserMapperTest.java
)
public class UserMapperTest {
@Test
public void getUsers() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.getUsers(1);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2.5 注意点
在pom.xml
的build
中配置resources
,来防止我们资源在Maven
项目中的导出失败的问题!
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
可能会遇到的问题:
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven导出资源问题
# 三、生命周期和作用域
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
- 局部变量
- 这个类可以被实例化、使用和丢弃,它一旦创建了 SqlSessionFactory,就不再需要它了。
SqlSessionFactory
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。
- 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
- 可以想象为:数据库连接池
SqlSession
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源将被占用!
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
这里面的每一个Mapper
都代表一个具体的业务。
# 四、配置文件之基本优化🎋
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性) (opens new window)
- settings(设置) (opens new window)
- typeAliases(类型别名) (opens new window)
- typeHandlers(类型处理器) (opens new window)
- objectFactory(对象工厂) (opens new window)
- plugins(插件) (opens new window)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识) (opens new window)
- mappers(映射器) (opens new window)
# 3.1 mybatis-config.xml配置文件属性优化
mybatis-config.xml
中拥有数据库的账号密码,安全起见,不能直接加入到编译文件中,新建一个db.properties
,将驱动、URL、账号、密码等数据存储到这里,然后在mybatis-config.xml
调用即可。
<!--加载properties文件到MyBatis配置文件中-->
<properties resource="db.properties"></properties>
2
【示例】:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载properties文件到MyBatis配置文件中-->
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="com/singerw/mapper/UserMapper.xml"/>
<mapper resource="com/singerw/mapper/ArticleMapper.xml"/>
</mappers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
【示例】:db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/goku?serverTimezone=Asia/Shanghai
username=root
password=795200
2
3
4
# 3.2 类型别名优化
在Mapper XML中使用实体类时候,每一次都需要从com.singerw.pojo.XXX中加载拿取使用,例如resultType="Article"的等等,为方便起见,直接在取别名在mybatis-config.xml文件中给该实体类取别名,方便使用,也有其他的作用。
# 🅰️方案一、DIY别名
【示例】mybatis-config.xml
<!-- 别名 -->
<typeAliases>
<typeAlias type="com.singerw.pojo.User" alias="User" />
<typeAlias type="com.singerw.pojo.Article" alias="Article" />
</typeAliases>
2
3
4
5
【示例】基本使用
<mapper namespace="com.singerw.mapper.ArticleMapper">
<!--查询所有文章-->
<select id="getArtilceList" resultType="Article">
select *
from artilce;
</select>
</mapper>
2
3
4
5
6
7
# 🅱️方案二、扫描包别名
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.singerw.pojo"/>
</typeAliases>
2
3
会扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
- 实体类比较少的时候建议使用第一种。
- 实体类比较多的时候建议使用第二种。
# 五、MyBatis实现CRUD⛵️
# 4.1 XML形式的CURD
# 1、增加操作
# 🅰️方案一、通过对象插入
【示例:UserMapper.java
】
public interface UserMapper {
boolean addUser(User user);
}
2
3
4
【示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--增加用户-->
<insert id="addUser">
insert into g_users
value (null,
#{username},
#{userphone},
#{userpassword},
0,
now(),
now(),
1)
</insert>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void addUser() {
User user = new User("张欣", "19999999999", "assam1314520");
boolean flag = userMapper.addUser(user);
sqlSession.commit();
System.out.println(flag ? "增加成功" : "增加失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 🅱️方案二、通过Map插入
💛(万能方法)工作必备野路子方法,也可以使用到查询,修改,删除等等中💛
假设我们的实体类中,或者数据库的表中,字段或者参数很多,我们应当考虑使用Map
插入
【示例:UserMapper.java
】
public interface UserMapper {
boolean addUser2(Map<String,Object> map);
}
2
3
4
【示例: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">
<mapper namespace="com.singerw.mapper.UserMapper">
<!--增加用户-->
<insert id="addUser2" parameterType="map">
insert into g_users (userid, username, userphone, userpassword, jurisdiction, createtime, logintime, userstatus)
values (null,#{username},#{userphone},#{userpassword},#{jurisdiction},now(),now(),#{userstatus});
</insert>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void addUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap hashMap = new HashMap<String,Object>();
hashMap.put("username","singerw");
hashMap.put("userphone","18888888888");
hashMap.put("userpassword","123456");
hashMap.put("jurisdiction",1);
hashMap.put("userstatus",1);
mapper.addUser2(hashMap);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2、删除操作
【示例:UserMapper.java
】
public interface UserMapper {
boolean delUser(int userid);
}
2
3
4
【示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--根据ID删除用户-->
<delete id="delUser">
delete
from goku.g_users
where userid = #{userid}
</delete>
</mapper>
2
3
4
5
6
7
8
9
10
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void delUser() {
boolean flag = userMapper.delUser(10);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "删除成功" : "删除失败");
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3、修改操作
# 🅰️方案一、参数为一个对象
【示例:UserMapper.java
】
public interface UserMapper {
boolean updateUser2(User user);
}
2
3
4
【示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--根据ID修改用户-->
<update id="updateUser">
update g_users
set username = #{username},
userphone = #{userphone},
userpassword = #{userpassword},
jurisdiction = #{jurisdiction},
userstatus = #{userstatus}
where userid = #{userid}
</update>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void updateUser2() {
User user = new User("测试测试测试", "156515561561", "123456", 1, 1, 8);
boolean flag = userMapper.updateUser2(user);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "修改成功" : "修改失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 🅱️方案二、参数为多个不同参数
注意:此时我们的修改的方法的参数不再是单一的参数,而是多个参数
【示例:UserMapper.java
】
public interface UserMapper {
boolean updateUser(String username, String userphone, String userpassword, int jurisdiction, int userstatus, int userid);
}
2
3
4
【示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--根据ID修改用户-->
<update id="updateUser">
update g_users
set username = #{username},
userphone = #{userphone},
userpassword = #{userpassword},
jurisdiction = #{jurisdiction},
userstatus = #{userstatus}
where userid = #{userid}
</update>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void updateUser() {
boolean flag = userMapper.updateUser("测试测试测试", "1444444444", "123456", 1, 1, 5);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "修改成功" : "修改失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
报错处理
这时我们发现测试结果报错:
Parameter 'username' not found. Available parameters are [arg3, arg2, param5, arg5, arg4, param6, arg1, arg0, param3, param4, param1, param2]
多个参数的绑定默认按照顺序;
🅰️解决方法一(推荐):修改UserMapper.xml
中的update
参数信息为如下,MyBatis才能识别到。
【解决方法示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--根据ID修改用户-->
<update id="updateUser">
update g_users
set username = #{arg0},
userphone = #{arg1},
userpassword = #{arg2},
jurisdiction = #{arg3},
userstatus = #{arg4}
where userid = #{arg5}
</update>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
或者修改UserMapper.java
中的updateUser
方法的参数信息,这样MyBatis也能识别到。
public interface UserMapper {
boolean updateUser(@Param("username") String username, @Param("userphone") String userphone, @Param("userpassword") String userpassword, @Param("jurisdiction") int jurisdiction, @Param("userstatus") int userstatus, @Param("userid") int userid);
}
2
3
4
5
🅱️解决方法二:将参数构造成一个map
对象,将我们的属性传递进来
【解决方法示例:UserMapper.java
】
public interface UserMapper {
boolean updateUser(Map map);
}
2
3
4
【解决方法示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--根据ID修改用户-->
<!--根据ID修改用户-->
<!--根据ID修改用户-->
<update id="updateUser">
update g_users
set username = #{username},
userphone = #{userphone},
userpassword = #{userpassword},
jurisdiction = #{jurisdiction},
userstatus = #{userstatus}
where userid = #{userid}
</update>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【解决方法示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void updateUser() {
HashMap map = new HashMap();
map.put("username","测试测试测试");
map.put("userphone","1444444444");
map.put("userpassword","123456");
map.put("jurisdiction",1);
map.put("userstatus",0);
map.put("userid",10);
boolean flag = userMapper.updateUser(map);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "修改成功" : "修改失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4 、查询操作
【示例:UserMapper.java
】
public interface UserMapper {
List<User> getUsers();
User getUser(int userid);
}
2
3
4
5
6
【示例:UserMapper.xml
】
<mapper namespace="com.singerw.mapper.UserMapper">
<!--查询所有用户-->
<!-- 此时没有指定包的情况,其实用的是别名Blog -->
<select id="getUsers" resultType="User">
select *
from goku.g_users
</select>
<!-- 此时没有指定包的情况,其实用的是别名Blog -->
<select id="getUser" resultType="User">
select *
from goku.g_users
where userid = #{userid}
</select>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void getUsers() {
List<User> users = userMapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUser() {
User user = userMapper.getUser(2);
System.out.println(user);
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4.2 Annotation注解形式的CURD
# 1、增加操作
【示例:UserMapper.java
】
public interface UserMapper {
@Insert(value = "INSERT INTO g_users VALUE (NULL,#{username},#{userphone},#{userpassword},0,now(),now(),1)")
boolean addUser(User user);
}
2
3
4
5
6
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void addUser() {
User user = new User("张欣", "5465432543453", "assam1314520");
boolean flag = userMapper.addUser(user);
sqlSession.commit();
System.out.println(flag ? "增加成功" : "增加失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2、删除操作
【示例:UserMapper.java
】
public interface UserMapper {
@Delete(value = "DELETE FROM goku.g_users WHERE userid=#{userid}")
boolean delUser(int userid);
}
2
3
4
5
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void delUser() {
boolean flag = userMapper.delUser(15);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "删除成功" : "删除失败");
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3、修改操作
【示例:UserMapper.java
】
public interface UserMapper {
@Update(value = "UPDATE g_users SET username=#{username},userphone=#{userphone},userpassword=#{userpassword},jurisdiction=#{jurisdiction},userstatus=#{userstatus} WHERE userid=#{userid}")
boolean updateUser(@Param("username") String username, @Param("userphone") String userphone, @Param("userpassword") String userpassword, @Param("jurisdiction") int jurisdiction, @Param("userstatus") int userstatus, @Param("userid") int userid);
}
2
3
4
5
6
7
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void updateUser() {
boolean flag = userMapper.updateUser("测试测试测试", "1444444444", "123456", 1, 1, 5);
//必须要提交
sqlSession.commit();
System.out.println(flag ? "修改成功" : "修改失败");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4、查询操作
【示例:UserMapper.java
】
public interface UserMapper {
@Select(value = "select * from goku.g_users")
List<User> getUsers();
@Select(value = "select * from goku.g_users where userid = #{userid}")
User getUser(int userid);
}
2
3
4
5
6
7
8
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void getUsers() {
List<User> users = userMapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUser() {
User user = userMapper.getUser(2);
System.out.println(user);
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4.3 万能Map🌊
💛(万能方法)工作必备野路子方法,也可以使用到查询,修改,删除等等中💛
假设我们的实体类中,或者数据库的表中,字段或者参数很多,我们应当考虑使用Map
插入
【示例:UserMapper.java
】
public interface UserMapper {
boolean addUser2(Map<String,Object> map);
}
2
3
4
【示例: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">
<mapper namespace="com.singerw.mapper.UserMapper">
<!--增加用户-->
<insert id="addUser2" parameterType="map">
insert into g_users (userid, username, userphone, userpassword, jurisdiction, createtime, logintime, userstatus)
values (null,#{username},#{userphone},#{userpassword},#{jurisdiction},now(),now(),#{userstatus});
</insert>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
【示例:UserMapperTest.java
】
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void addUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap hashMap = new HashMap<String,Object>();
hashMap.put("username","singerw");
hashMap.put("userphone","18888888888");
hashMap.put("userpassword","123456");
hashMap.put("jurisdiction",1);
hashMap.put("userstatus",1);
mapper.addUser2(hashMap);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 六、Mapper XML 文件🎯
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
cache
– 给定命名空间的缓存配置。cache-ref
– 其他命名空间缓存配置的引用。resultMap
– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。parameterMap
– 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。sql
– 可被其他语句引用的可重用语句块。insert
– 映射插入语句update
– 映射更新语句delete
– 映射删除语句select
– 映射查询语句
属性 | 描述 |
---|---|
keyProperty | selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn | 匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
resultType | 结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 |
order | 这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素 - 这和像 Oracle 的数据库相似,在插入语句内部可能有嵌入索引调用。 |
statementType | 与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。 |
# 七、ResultMat的使用-xml🌊
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBCResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。- 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份
resultMap
能够代替实现同等功能的数千行代码。 - ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
# 6.1 简单结果集映射
解决属性名和字段名不一致的问题
【示例】数据库中的User
表字段
- userid
- username
- userphone
- userpassword
- jurisdiction
- createtime
- logintime
- userstatus
数据库中的字段和实体类中的字段不一致。
【示例】User.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String phone;
private String password;
private int juri;
private String created;
private String logined;
private int status;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用结果集映射
【示例】UserMapper.xml
<mapper namespace="com.singerw.mapper.UserMapper">
<!--查询所有用户-->
<select id="getUsers" resultMap="userMap">
select *
from goku.g_users
</select>
<!--根据ID查询用户-->
<select id="getUser" resultMap="userMap">
select *
from goku.g_users
where userid = #{userid}
</select>
<!--resultMap结果集映射-->
<resultMap id="userMap" type="User">
<!-- 实体类和表列做关联-->
<!-- id 主键 属性 -->
<id property="id" column="userid"></id>
<!--其他部分-->
<!--column数据库中的字段,property实体类中的属性-->
<result property="name" column="username"></result>
<result property="phone" column="userphone"></result>
<result property="password" column="userpassword"></result>
<result property="jurisdiction" column="jurisdiction"></result>
<result property="juri" column="createtime"></result>
<result property="created" column="createtime"></result>
<result property="logined" column="logintime"></result>
<result property="status" column="userstatus"></result>
</resultMap>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
【示例】UserMapperTest.java
public class UserMapperTest {
private SqlSession sqlSession = MyBatisUtils.getSqlSession();
private UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
@Test
public void getUsers() {
List<User> users = userMapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void getUser() {
User user = userMapper.getUser(2);
System.out.println(user);
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 6.3 Association完整案例
一个复杂类型的关联,按照结果嵌套查询,多对一。
🌊(以1:N为案例关联映射)🌊
在数据库设计中,普遍存在一对多,多对一的表关系。blogdb数据库中三个基本表结构如下 。
在数据库表设计的过程中,建立了如上的表结构,那么到程序设计中,实体类应该怎么写呢?
一个作者可以发表多篇文章,而发表的这些多篇文章只能有一个作者,这里文章和作者是1:N的关系。
Article(N)
:如果需要得到一篇或多篇文章的作者,需要在实体类中增加一个类型。
private Users users
User(1)
:在user表中查询某一用户的时候,同时想得到这个用户发表了哪些文章,需要在实体类中增加一个集合。
private List<Article> articleList
通过关键字或者文章ID查询一篇或多篇文章和每篇文章的作者信息
# 步骤一、编写实体类
【实体类示例】:Article.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {
private int id;
private String title;
private String content;
private String summary;
private int cid;
private Users users;
private String created;
private int status;
}
2
3
4
5
6
7
8
9
10
11
12
13
【实体类示例】:Users.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String username;
private String nickname;
private String password;
private int status;
private String email;
private String userface;
private String created;
private String lastlogin;
private List<Article> articleList;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 步骤二、实现数据查询操作
【SQL语句示例】
<!--使用了resultMap,需要在xml中创建一个id为articleMap的resultMap-->
<select id="getArticles" resultMap="articleMap">
SELECT article.id,
article.title,
article.content,
article.summary,
article.created,
article.`status`,
`user`.id AS uid,
`user`.username,
`user`.nickname
FROM article
INNER JOIN `user` ON article.uid = `user`.id
WHERE article.title LIKE #{title}
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 步骤三、编写查询方法接口
【查询方法示例】:ArticleMapper.java
public interface ArticleMapper {
List<Article> getArticles(String title);
}
2
3
4
# 步骤四、编写mapper.xml中的ResultMap
【ResultMap关联示例】:ArticleMapper.xml
<!-- 映射关系的管理和定义 ,type指的是实体类型(取了别名),id当前的resultMap的名字 -->
<resultMap type="Article" id="articleMap">
<!-- 主键使用id -->
<id property="id" column="id"/>
<!-- 一个result表示一个列和实体类的属性的对应关系 -->
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="summary" column="summary"/>
<result property="created" column="created"/>
<result property="status" column="status"/>
<!-- 此时我们的Article类中,增加了一个Users类型的属性 -->
<association property="users" column="uid" javaType="com.singerw.pojo.Users">
<!-- author类对应的表中的列 和 类属性关联 -->
<id property="id" column="uid"/>
<result property="username" column="username"/>
<result property="nickname" column="nickname"/>
</association>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 步骤五、进行单元测试
public class ArticleMapperTest {
@Test
public void getArticles() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
ArticleMapper mapper = sqlSession.getMapper(ArticleMapper.class);
List<Article> articles = mapper.getArticles("%VUE%");
articles.forEach(System.out::println);
}
}
2
3
4
5
6
7
8
9
10
Article(id=2, title=VUE, content=VUE是极简的前端框架, summary=vue, category=null, users=Users(id=1, username=tom, nickname=唐木松, password=null, status=0, email=null, userface=null, created=null, lastlogin=null, articleList=null, categoriesList=null), created=2020-12-25 01:20:20, status=1)
# 6.4 Collection节点案例
一个复杂类型的集合。按照结果嵌套查询,一对多。
🌊(以1:N为案例关联映射)🌊
在数据库设计中,普遍存在一对多,多对一的表关系。blogdb数据库中三个基本表结构如下 。
在数据库表设计的过程中,建立了如上的表结构,那么到程序设计中,实体类应该怎么写呢?
一个作者可以发表多篇文章,而发表的这些多篇文章只能有一个作者,这里文章和作者是1:N的关系。
Article(N)
:如果需要得到一篇或多篇文章的作者,需要在实体类中增加一个类型。
private Users users
User(1)
:在user表中查询某一用户的时候,同时想得到这个用户发表了哪些文章,需要在实体类中增加一个集合。
private List<Article> articleList
通过用户ID查询用户和用户发表的文章
# 步骤一、编写实体类
【实体类示例】:Article.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {
private int id;
private String title;
private String content;
private String summary;
private int cid;
private Users users;
private String created;
private int status;
}
2
3
4
5
6
7
8
9
10
11
12
13
【实体类示例】:Users.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String username;
private String nickname;
private String password;
private int status;
private String email;
private String userface;
private String created;
private String lastlogin;
private List<Article> articleList;
private List<Category> categoriesList;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 步骤二、实现数据查询操作
【SQL语句示例】
SELECT `user`.id AS userid,
`user`.username,
`user`.nickname,
`user`.`password`,
`user`.`status` AS userstatus,
`user`.email,
`user`.userface,
`user`.created AS user_created,
`user`.lastlogin,
article.id AS articleid,
article.title,
article.content,
article.summary,
article.created,
article.STATUS AS articlestatus
FROM `user`
INNER JOIN article ON `user`.id = article.uid
WHERE `user`.id = #{id}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 步骤三、编写查询方法接口
【查询方法示例】:UsersMapper.java
public interface UsersMapper {
<List>Users getUserAndArticle(int id);
}
2
3
4
# 步骤四、编写mapper.xml中的ResultMap
【ResultMap关联示例】:UsersMapper.xml
<!-- 映射关系的管理和定义 ,type指的是实体类型,id当前的resultMap的名字 -->
<resultMap type="Users" id="userMap2">
<!-- 主键使用id -->
<id property="id" column="userid"/>
<!-- 一个result表示一个列和实体类的属性的对应关系 -->
<result property="username" column="username"/>
<result property="nickname" column="nickname"/>
<result property="password" column="password"/>
<result property="status" column="userstatus"/>
<result property="email" column="email"/>
<result property="userface" column="userface"/>
<result property="created" column="user_created"/>
<result property="lastlogin" column="lastlogin"/>
<collection property="articleList" ofType="Article">
<id property="id" column="articleid"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="summary" column="summary"/>
<result property="created" column="category_created"/>
<result property="status" column="articlestatus"/>
</collection>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 步骤五、进行单元测试
public class UsersMapperTest {
@Test
public void getUserAndArticle() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
Users user = mapper.getUserAndArticle(2);
System.out.println(user);
}
}
2
3
4
5
6
7
8
9
10
Users(id=2, username=jerry, nickname=吴八阁, password=e10adc3949ba59abbe56e057f20f883e, status=1, email=jerry@qq.com, userface=jerry.jpg, created=2021-01-08 01:01:22, lastlogin=2020-12-08 01:01:22, articleList=[Article(id=1, title=SpringBoot, content=SpringBoot是spring全家桶中很重要的一员, summary=spring, category=null, users=null, created=null, status=1), Article(id=5, title=MySQL, content=VUE是极简的前端框架, summary=vue, category=null, users=null, created=null, status=1), Article(id=7, title=Dubbo, content=VUE是极简的前端框架, summary=vue, category=null, users=null, created=null, status=1), Article(id=8, title=RabbitMQ, content=VUE是极简的前端框架, summary=vue, category=null, users=null, created=null, status=1), Article(id=10, title=Oracle, content=VUE是极简的前端框架, summary=vue, category=null, users=null, created=null, status=1), Article(id=12, title=Layui, content=VUE是极简的前端框架, summary=vue, category=null, users=null, created=null, status=1)], categoriesList=null)
# 6.5 ResultMap使用常见问题
1、使用别名时,要创建别名
<resultMap type="Users" id="userMap2">
<!-- 别名 -->
<typeAliases>
<typeAlias type="com.singerw.pojo.Users" alias="Users" />
</typeAliases>
2
3
4
2、使用resultMap
时需要先创建一个resultMap
<select id="getCategoryAndArticle" resultMap="categoryMap">
...
</select>
2
3
<resultMap id="categoryMap" type="Category">
...
</resultMap>
2
3
3、resultMap
中的每张表都要求都有主键
<id property="id" column="id"/>
<resultMap id="categoryMap" type="Category">
<!--主键-->
<id property="id" column="id"/>
<!--其他部分-->
<result property="catename" column="catename"/>
...
<collection property="articleList" ofType="Article">
<result property="id" column="art_id"/>
...
</collection>
</resultMap>
2
3
4
5
6
7
8
9
10
11
4、result
中property
需要实体类中的属性名,column
是表中的类名。
<result property="id" column="art_id"/>
5、JavaType
:是用来指定实体类中属性的类型。
6、ofType
:是用来指定映射到List或者集合中的实体类类型,泛型中的约束类型!
# 八、 ResultMap的使用-annotation
# 8.1 简单结果集映射使用
【示例】实体类Article.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article implements Serializable {
private int id;
private String artTitle;
private String artContent;
private String artSummary;
private String artCreated;
private int artStatus;
}
2
3
4
5
6
7
8
9
10
11
【示例】mybatis-config.xml
<mappers>
<mapper class="com.singerw.mapper.ArticleMapper"/>
</mappers>
2
3
【示例】mapper.java
接口
public interface ArticleMapper {
@Results(id = "articleResults",value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "artTitle",column = "title"),
@Result(property = "artContent",column = "content"),
@Result(property = "artSummary",column = "summary"),
@Result(property = "artStatus",column = "status"),
})
@Select(value = "select * from article where id = #{id}")
Article getArticleByID(@Param("id") int id);
}
2
3
4
5
6
7
8
9
10
11
上方定义的Results
也可以引用到别的方法上。如下:
public interface ArticleMapper {
//这里的ResultMap是引用在注解中已经定义好的某个Results的ID
@ResultMap(value = "articleResults")
@Select(value = "select * from article")
List<Article> getAllArticle();
}
2
3
4
5
6
7
8
# 8.2 高级结果集映射
# 八、日志⚡️
# 7.1 日志工厂
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
【示例】mybatis-config.xml
中配置setting
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2
3
# 7.2 Log4j的基本使用
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
# 步骤一:导入Jar文件
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
2
3
4
5
6
# 步骤二:配置log4j配置文件
新建log4j.properties
,进行配置
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=%-d{yyyy-MM-dd HH:mm:ss} [%p][%c{1}] - %m%n
log4j.appender.console.Encoding=UTF-8
#文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./logs/log.log
log4j.appender.file.MaxFileSize=500KB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%p]-%c{1} - %m%n
log4j.appender.file.encoding=UTF-8
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
log4j.rootCategory=INFO, stdout , R
此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名。等级可分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO、WARN、ERROR的log信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger。
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
此句为定义名为stdout的输出端是哪种类型,可以是
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
具体讲解可参照第三部分定义配置文件中的Appender。
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
此句为定义名为stdout的输出端的layout是哪种类型,可以是
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
具体讲解可参照第三部分定义配置文件中的Layout。
log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n
如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:
%m
输出代码中指定的消息;%M
输出打印该条日志的方法名;%p
输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL;%r
输出自应用启动到输出该log信息耗费的毫秒数;%c
输出所属的类目,通常就是所在类的全名;%t
输出产生该日志事件的线程名;%n
输出一个回车换行符,Windows平台为"rn”,Unix平台为"n”;%d
输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2002-10-18 22:10:28,921;%l
输出日志事件的发生位置,及在代码中的行数;[QC]
是log信息的开头,可以为任意字符,一般为项目简称。输出的信息
[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean 'MyAutoProxy'
具体讲解可参照第三部分定义配置文件中的格式化日志信息。
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
此句与第3行一样。定义名为R的输出端的类型为每天产生一个日志文件。
log4j.appender.R.File=D:\Tomcat 5.5\logs\qc.log
此句为定义名为R的输出端的文件名为D:\Tomcat 5.5\logs\qc.log可以自行修改。
log4j.appender.R.layout=org.apache.log4j.PatternLayout
与第4行相同。
log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
与第5行相同。
log4j.logger.com. neusoft =DEBUG
指定com.neusoft包下的所有类的等级为DEBUG。
可以把com.neusoft改为自己项目所用的包名。
log4j.logger.com.opensymphony.oscache=ERROR
og4j.logger.net.sf.navigator=ERROR
这两句是把这两个包下出现的错误的等级设为ERROR,如果项目中没有配置EHCache,则不需要这两句。
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN
这两句是struts的包。
log4j.logger.org.displaytag=ERROR
这句是displaytag的包。(QC问题列表页面所用)
log4j.logger.org.springframework=DEBUG
此句为Spring的包。
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG
此两句是hibernate的包。
以上这些包的设置可根据项目的实际情况而自行定制。
# 步骤三:配置log4j为日志实现
在mybatis-config.xm
文件中配置log4j
为日志实现
<!--配置log4j为日志实现-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
2
3
4
# 步骤四:log4j的使用
- 在要使用Log4j的类中,导入
import org.apache.log4j.Logger;
- 创建日志对象,参数为当前类的
class
在要输出日志的类中加入相关语句来定义属性:
static Logger logger = Logger.getLogger(UserMapperTest.class);
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void testLog4j() {
logger.info("info:进入了testLog4j方法");
logger.debug("debug:进入了进入了testLog4j方法");
logger.error("debug:进入了进入了testLog4j方法");
}
}
2
3
4
5
6
7
8
9
10
2021-08-25 01:55:30 [DEBUG]-LogFactory - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. 2021-08-25 01:55:30 [DEBUG]-PooledDataSource - PooledDataSource forcefully closed/removed all connections. 2021-08-25 01:55:30 [DEBUG]-PooledDataSource - PooledDataSource forcefully closed/removed all connections. 2021-08-25 01:55:30 [DEBUG]-PooledDataSource - PooledDataSource forcefully closed/removed all connections. 2021-08-25 01:55:30 [DEBUG]-PooledDataSource - PooledDataSource forcefully closed/removed all connections. 2021-08-25 01:55:31 [INFO]-UserMapperTest - info:进入了testLog4j方法 2021-08-25 01:55:31 [DEBUG]-UserMapperTest - debug:进入了进入了testLog4j方法 2021-08-25 01:55:31 [ERROR]-UserMapperTest - debug:进入了进入了testLog4j方法
1
2
3
4
5
6
7
8
log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:
- OFF
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
- TRACE
- ALL
ALL
最低等级的,用于打开所有日志记录。TRACE
designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。DEBUG
指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。INFO
消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。WARN
表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。FATAL
指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。OFF
最高等级的,用于关闭所有日志记录。
如果将log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来。例如,如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出,而INFO、DEBUG、TRACE、 ALL级别的log则会被忽略。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。
# 九、动态SQL❌
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一些逻辑代码
# 8.1 if
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。比如:
【示例】ArticleMapper.java
public interface ArticleMapper {
List<Article> selectArticleByTitle(Article article);
}
2
3
4
5
【示例】Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.singerw.mapper.ArticleMapper">
<select id="selectArticleByTitle" resultType="article" parameterType="article">
select * from artilce where status = 1
<if test="title !=null">
and title like #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 8.2 choose(when, otherwise)
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
【示例】Mapper.xml
<select id="queryBlogChoose" parameterType="map" resultType="com.rui.pojo.Blog">
select * from mybatis.bolg
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author!=null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
【示例】Mapper.xml
# 8.3 trim(where, set)
【示例】Mapper.xml
select * from mybatis.bolg
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
2
3
4
5
6
7
8
9
【示例】Mapper.xml
<update id="updateBlog" parameterType="map">
update mybatis.bolg
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
2
3
4
5
6
7
8
9
10
11
12
# 8.4 foreach
可以做查询和删除还要批量删除。
【示例】ArticleMapper.xml
select * from user where 1=1 and
<foreach item="id" index="index" collection="ids"
open="(" separator="or" close=")">
#{id}
</foreach>
(id=1 or id=2 or id=3)
2
3
4
5
6
7
【示例】ArticleMapper.xml
<!--
select * from mybatis.bolg where 1=1 and (id=1 or id=2 or id=3)
我们现在传递一个万能的map,这个map中可以存在一个map
-->
<select id="queryBlogForeach" parameterType="map" resultType="com.rui.pojo.Blog">
select * from mybatis.bolg
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
12
13
# 8.5 SQL片段
有的时候,我们可能会将一些公共的部分抽取出来,方便复用!
使用SQL标签抽取公共的部分
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
1
2
3
4
5
6
7
8在需要使用的地方使用Include标签引用即可
<select id="queryBlogIF" parameterType="map" resultType="com.rui.pojo.Blog"> select * from mybatis.bolg <where> <include refid="if-title-author"></include> </where> </select>
1
2
3
4
5
6注意事项:
- 最好基于单表来定义SQL片段!
- 不要存在where或者set标签,片段里尽量只有if就好了
# 8.6 完整动态sql案例
# 十、实现分页功能🤕
# 9.1 Limit实现分页
通过SQL的Limit实现基本的分页功能。SQL层面的分页实现。
【示例】接口
public interface ArticleMapper {
List<Article> getArticlesByLimit(Map<String,Integer> map);
}
2
3
4
【示例】Mapper.xml
<mapper namespace="com.singerw.mapper.ArticleMapper">
<select id="getArticlesByLimit" resultType="Article" parameterType="map">
select * from article limit #{page},#{pageSize};
</select>
</mapper>
2
3
4
5
【示例】单元测试
public class ArticleMapperTest {
@Test
public void getArticleByLimit() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
ArticleMapper mapper = sqlSession.getMapper(ArticleMapper.class);
HashMap<String, Integer> map = new HashMap<>();
map.put("page",0);
map.put("pageSize",10);
List<Article> articles = mapper.getArticlesByLimit(map);
articles.forEach(System.out::println);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 9.2 RowBounds实现分页
通过Java代码层面实现分页功能。
【示例】接口
public interface ArticleMapper {
List<Article> getArticlesByRowBounds();
}
2
3
4
【示例】Mapper.xml
<mapper namespace="com.singerw.mapper.ArticleMapper">
<select id="getArticlesByRowBounds" resultType="Article">
select * from article;
</select>
</mapper>
2
3
4
5
【示例】单元测试
public class ArticleMapperTest {
@Test
public void getArticleByRowBounds() {
RowBounds rowBounds = new RowBounds(0,10);
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
List<Article> articles = sqlSession.selectList("com.singerw.mapper.ArticleMapper.getArticlesByRowBounds",null,rowBounds);
articles.forEach(System.out::println);
sqlSession.close();
}
}
2
3
4
5
6
7
8
9
10
11
# 9.3 MyBatis 分页插件 PageHelper实现分页
如果你也在用 MyBatis,建议尝试该分页插件,这一定是最方便使用的分页插件。分页插件支持任何复杂的单表、多表分页。
文档:https://pagehelper.github.io/docs/
# 步骤一:导入jar包
<!--pagehelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
2
3
4
5
6
# 步骤二:在 MyBatis 配置 xml 中配置拦截器插件
<!--PageHelper分页插件-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param" value="value"/>
</plugin>
</plugins>
2
3
4
5
6
7
8
# 步骤三:在 Spring 配置文件中配置拦截器插件
<!--分页拦截器插件-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注意其他配置 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<!--使用下面的方式配置参数,一行配置一个 -->
<value>
<!--params=value1-->
</value>
</property>
</bean>
</array>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
params
:为了支持startPage(Object params)
方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable
,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
。
# PageHelper官方使用案例
【例一】
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页
List<User> list = userMapper.selectIf(1);
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>
assertEquals(182, ((Page) list).getTotal());
2
3
4
5
6
7
8
【例二】
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# PageHelper实现分页小案例
【示例 】Mapper.java
@Repository
public interface ArticleMapper {
List<Article> getArticleListByLike(@Param("keywords") String keywords);
}
2
3
4
【示例 】Mapper.xml
<mapper namespace="com.singerw.dao.ArticleMapper">
<select id="getArticleListByLike" resultMap="artilceMap">
SELECT g_article.article_id,
g_article.article_title,
g_article.article_content,
g_article.acticle_img,
g_article.acticle_author,
g_article.acticle_type,
g_article.acticle_visits,
g_article.acticle_time,
g_article.acticle_status,
g_acticletype.a_id,
g_acticletype.a_type,
g_acticletype.act_type
FROM g_article
INNER JOIN
g_acticletype
ON
g_article.acticle_type = g_acticletype.act_type
WHERE
g_article.article_title LIKE #{keywords}
</select>
<resultMap id="artilceMap" type="article">
<id property="artID" column="article_id"/>
<result property="artTitle" column="article_title"/>
<result property="artContent" column="article_content"/>
<result property="artImg" column="acticle_img"/>
<result property="artAuthor" column="acticle_author"/>
<result property="artVisits" column="acticle_visits"/>
<result property="artCreateTime" column="acticle_time"/>
<result property="artStatus" column="acticle_status"/>
<association property="articleType" column="acticle_type">
<id property="typeID" column="a_id"/>
<result property="typeTitle" column="a_type"/>
<result property="typeNumber" column="act_type"/>
</association>
</resultMap>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
【示例 】Service.java
public interface ArticleService {
ResponseData<Article> getArticleListByLike(String keywords,int page,int pageSize);
}
2
3
【示例 】ServiceImpl.java
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Override
public ResponseData<Article> getArticleListByLike(String keywords,int page,int pageSize) {
if (keywords != null) {
keywords = "%" + keywords + "%";
}
PageHelper.startPage(page,pageSize);
List<Article> articleList = articleMapper.getArticleListByLike(keywords);
PageInfo pageInfo = new PageInfo(articleList);
ResponseData<Article> responseData = new ResponseData<Article>(0, "查询成功", pageInfo.getTotal(), articleList);
return responseData;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
【示例 】Controller.java
@RestController
@RequestMapping("/api")
public class ArticleController {
@Autowired
private ArticleService articleService;
@GetMapping("/artAll")
public ResponseData<Article> getArticleList1(
@RequestParam(name = "page", required = true, defaultValue = "0") int page,
@RequestParam(name = "limit", required = true, defaultValue = "10") int limit,
@RequestParam(name = "keywords", required = true, defaultValue = "") String keywords) {
ResponseData<Article> articleList = articleService.getArticleListByLike(keywords, page, limit);
return articleList;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 十一、MyBatis缓存🤙
# 11.1 缓存
什么事缓存[Cache]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,
从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
# 11.2 MyBatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
# 11.3 一级缓存
一级缓存也叫本地缓存:SqlSession,SqlSession级别的缓存,也称为本地缓存
- 与数据库同义词会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没有必要再去查询数据;
【基本使用步骤示例】
- 开启日志!
- 测试在一个Session中查询两次相同的记录
- 查看日志输出
缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的
Mapper.xml
- 手动清理缓存!
sqlsession.clearCache(); //手动清理缓存
小节:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!
一级缓存就是一个Map。
@Test
public void getArticles() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
ArticleMapper mapper = sqlSession.getMapper(ArticleMapper.class);
List<Article> articles1 = mapper.getArticles("%VUE%");
for (Article article : articles1) {
System.out.println(article);
}
List<Article> articles2 = mapper.getArticles("%VUE%");
for (Article article : articles2) {
System.out.println(article);
}
System.out.println(articles1==articles2);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
2021-08-26 15:14:58 [DEBUG][getArticles] - ==> Preparing: SELECT article.id, article.title, article.content, article.summary, article.created, article.
status
, category.id AS cid, category.catename, category.created AS ccreated,user
.id AS uid,user
.username,user
.nickname FROM article INNER JOIN category ON article.cid = category.id INNER JOINuser
ON article.uid =user
.id WHERE article.title LIKE ?2021-08-26 15:14:58 [DEBUG][getArticles] - ==> Parameters: %VUE%(String)
2021-08-26 15:14:59 [DEBUG][getArticles] - <== Total: 1
Article(id=2, title=VUE, content=VUE是极简的前端框架, summary=vue, category=null, users=Users(id=1, username=tom, nickname=唐木松, password=null, status=0, email=null, userface=null, created=null, lastlogin=null, articleList=null, categoriesList=null), created=2020-12-25 01:20:20, status=1)
Article(id=2, title=VUE, content=VUE是极简的前端框架, summary=vue, category=null, users=Users(id=1, username=tom, nickname=唐木松, password=null, status=0, email=null, userface=null, created=null, lastlogin=null, articleList=null, categoriesList=null), created=2020-12-25 01:20:20, status=1) true
@Test
public void getArticles() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
ArticleMapper mapper1 = sqlSession.getMapper(ArticleMapper.class);
List<Article> articles1 = mapper1.getArticles("%VUE%");
for (Article article : articles1) {
System.out.println(article);
}
ArticleMapper mapper2 = sqlSession.getMapper(ArticleMapper.class);
List<Article> articles2 = mapper2.getArticles("%VUE%");
for (Article article : articles2) {
System.out.println(article);
}
System.out.println(articles1==articles2);
System.out.println("mapper1:"+mapper1);
System.out.println("mapper2:"+mapper2);
System.out.println(mapper1==mapper2);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2021-08-26 15:17:37 [DEBUG][getArticles] - ==> Preparing: SELECT article.id, article.title, article.content, article.summary, article.created, article.
status
, category.id AS cid, category.catename, category.created AS ccreated,user
.id AS uid,user
.username,user
.nickname FROM article INNER JOIN category ON article.cid = category.id INNER JOINuser
ON article.uid =user
.id WHERE article.title LIKE ?2021-08-26 15:17:37 [DEBUG][getArticles] - ==> Parameters: %VUE%(String)
2021-08-26 15:17:37 [DEBUG][getArticles] - <== Total: 1
Article(id=2, title=VUE, content=VUE是极简的前端框架, summary=vue, category=null, users=Users(id=1, username=tom, nickname=唐木松, password=null, status=0, email=null, userface=null, created=null, lastlogin=null, articleList=null, categoriesList=null), created=2020-12-25 01:20:20, status=1)
Article(id=2, title=VUE, content=VUE是极简的前端框架, summary=vue, category=null, users=Users(id=1, username=tom, nickname=唐木松, password=null, status=0, email=null, userface=null, created=null, lastlogin=null, articleList=null, categoriesList=null), created=2020-12-25 01:20:20, status=1)
true
mapper1:org.apache.ibatis.binding.MapperProxy@3012646b mapper2:org.apache.ibatis.binding.MapperProxy@4a883b15 false
# 11.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据会被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
【基本使用步骤示例】
开启全局缓存
<!--显式的开启全局缓存--> <setting name="cacheEnabled" value="true"/>
1
2在要使用二级缓存的Mapper中开启
<!--在当前Mapper.xml中使用二级缓存--> <cache/> 也可以自定义参数 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
1
2
3
4
5
6
7
8
9
10测试
问题:我们需要将实体类序列化!否则就会报错
java.io.NotSerializableException: com.rui.pojo.User
1
小结:
- 只要开启了二级缓存,在同一个Mapper.java下就有效
- 所有的数据都会先放在一级缓存中;
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
# 11.6 自定义缓存-encache
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
要在程序中使用ehcache,先要导包!
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
2
3
4
5
然后在mapper中指定使用ehcache缓存实现
<!--在当前Mapper.xml中使用二级缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
2
导入配置文件 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 十四、mybatis中使用的配置文件示例
# 1、mybatis-config.xml完整配置文件示例
【示例】:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--加载properties文件到MyBatis配置文件中-->
<properties resource="db.properties"></properties>
<!--配置log4j为日志实现-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 别名 -->
<typeAliases>
<typeAlias type="com.singerw.pojo.Users" alias="Users" />
<typeAlias type="com.singerw.pojo.Category" alias="Category" />
<typeAlias type="com.singerw.pojo.Article" alias="Article" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="com/singerw/mapper/ArticleMapper.xml"/>
<mapper resource="com/singerw/mapper/UsersMapper.xml"/>
<mapper resource="com/singerw/mapper/CategoryMapper.xml"/>
</mappers>
</configuration>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
【示例】:db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/goku?serverTimezone=Asia/Shanghai
username=root
password=795200
2
3
4
# 2、映射器
<!--映射器-->
<!--每一个mapper.xml都需要在MyBatis核心配置文件中注册-->
<mappers>
<mapper resource="com/singerw/mapper/UserMapper.xml"/>
<mapper resource="com/singerw/mapper/ArticleMapper.xml"/>
</mappers>
2
3
4
5
6
<mappers>
<mapper class="com.singerw.mapper.ArticleMapper"/>
</mappers>
2
3
# 3、类型别名
<!-- 别名 -->
<typeAliases>
<typeAlias type="com.singerw.pojo.User" alias="User" />
<typeAlias type="com.singerw.pojo.Article" alias="Article" />
</typeAliases>
2
3
4
5
# 4、数据库链接信息属性
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/goku?serverTimezone=Asia/Shanghai
username=root
password=795200
2
3
4
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
2
3
4
5
6
7
8
9
10
11
# 5、ResultMat
【示例】数据库中的User
表字段
- userid
- username
- userphone
- userpassword
- jurisdiction
- createtime
- logintime
- userstatus
数据库中的字段和实体类中的字段不一致。
使用结果集映射
【示例】UserMapper.xml
<!--resultMap结果集映射-->
<resultMap id="userMap" type="User">
<!-- 实体类和表列做关联-->
<!-- id 主键 属性 -->
<id property="id" column="userid"></id>
<!--其他部分-->
<!--column数据库中的字段,property实体类中的属性-->
<result property="name" column="username"></result>
<result property="phone" column="userphone"></result>
<result property="password" column="userpassword"></result>
<result property="jurisdiction" column="jurisdiction"></result>
<result property="juri" column="createtime"></result>
<result property="created" column="createtime"></result>
<result property="logined" column="logintime"></result>
<result property="status" column="userstatus"></result>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
collection关联映射案例,通过用户ID查询用户和用户发表的文章
<!-- 映射关系的管理和定义 ,type指的是实体类型,id当前的resultMap的名字 -->
<resultMap type="Users" id="userMap2">
<!-- 主键使用id -->
<id property="id" column="userid"/>
<!-- 一个result表示一个列和实体类的属性的对应关系 -->
<result property="username" column="username"/>
<result property="nickname" column="nickname"/>
<result property="password" column="password"/>
<result property="status" column="userstatus"/>
<result property="email" column="email"/>
<result property="userface" column="userface"/>
<result property="created" column="user_created"/>
<result property="lastlogin" column="lastlogin"/>
<collection property="articleList" ofType="Article">
<id property="id" column="articleid"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="summary" column="summary"/>
<result property="created" column="category_created"/>
<result property="status" column="articlestatus"/>
</collection>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
association关联映射,通过关键字或者文章ID查询一篇或多篇文章和每篇文章的作者信息
<!-- 映射关系的管理和定义 ,type指的是实体类型(取了别名),id当前的resultMap的名字 -->
<resultMap type="Article" id="articleMap">
<!-- 主键使用id -->
<id property="id" column="id"/>
<!-- 一个result表示一个列和实体类的属性的对应关系 -->
<result property="title" column="title"/>
<result property="content" column="content"/>
<result property="summary" column="summary"/>
<result property="created" column="created"/>
<result property="status" column="status"/>
<!-- 此时我们的Article类中,增加了一个Users类型的属性 -->
<association property="users" column="uid" javaType="com.singerw.pojo.Users">
<!-- author类对应的表中的列 和 类属性关联 -->
<id property="id" column="uid"/>
<result property="username" column="username"/>
<result property="nickname" column="nickname"/>
</association>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 6、开启全局缓存
<!--显式的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
2
在要使用二级缓存的Mapper.xml中开启
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
也可以自定义参数
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
2
3
4
5
6
7
8
9
10
# 十五、MyBatisUitls.java工具类⚡️
package com.singerw.utils;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionUtil {
private static SqlSessionFactory sqlSessionFactory = null;
// 使用ThreadLocal 来管理我们的 SqlSession
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
static {
// 指定mybaits的全局配置文件的路径
String resource = "mybatis-config.xml";
// 输入流读取配置文件
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 通过FactoryBuilder 得到 SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 释放inputStream
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @return
*/
public static SqlSession getSqlSession() {
// 要先去threadLocal 看看是否有和当前线程绑定的那个sqlsession如果有 直接使用 没有再创建 op~ session
SqlSession session = threadLocal.get();
if (session == null) {
// 通过sqlSessionFactory得到SqlSession
session = sqlSessionFactory.openSession();
// 得到了这个session,就将其放在threadLocal中
threadLocal.set(session);
}
return session;
}
/**
*
*/
public static void closeSqlSession() {
// 从当前线程中获取 SqlSession对象
SqlSession session = threadLocal.get();
// 如果对象 不为空
if (session != null) {
// 从threadLocal 中移除
threadLocal.remove();
session.close();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72