本文原创首发CSDN,本文链接https://blog.csdn.net/qq_41464123/article/details/108172276作者博客https://blog.csdn.net/qq_41464123,转载请带上本链接,谢谢配合。
一、前置工作
1.1 MyBatis简介
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录。
- 官方文档:https://mybatis.org/mybatis-3/zh/index.html
1.2 个人点评分析
- MyBatis是一款优秀的持久层框架,通俗来说就是用来连接数据库。在没有JPA和MyBatis之前,原生JDBC式操作设置参数和获取结果集的工作非常臃肿枯燥。
- 在MyBatis中可以通过简单的 XML或者Java注解实现JDBC大量代码才能完成的操作。
- MyBatis简单易学,不需要第三方依赖,只需要看官方文档就可以学习。
- MyBatis将SQL语句放到XML文件内,统一管理和优化。SQL作为数据库领域的霸主,基本可以实现我们所有的功能,而MyBatis可以很方便使用原生SQL。
- 高内聚低耦合,SQL不放在Dao,使得系统设计清晰,而JPA没有做到这一点
- 在最近参与的项目编写中,采用了Xboot框架,其中整合了MyBatis和JPA。从实际使用性来说,MyBatis的优势远远大于JPA。
1.3 idea新建项目配置
- jdk 11.0.6
- maven 3.6.2 阿里云
- idea 2019.3
- 选择创建maven新项目,删除src目录,创建子module
- 在 File | Settings | Build, Execution, Deployment | Build Tools | Maven 中,配置maven
- 在 File | Settings | Build, Execution, Deployment | Compiler | Java Compiler 中,设置发行版本为 11
- File | Settings | Editor | File Encodings,编码改为UTF-8
- 在File | Project Setting | Modules 中,将版本设置为11
1.4 依赖、资源过滤
放在父 pom.xml 中即可
代码语言:javascript复制<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
1.5 创建数据库
因为关联外键、异步等原因,必须分步执行,否则会出错
代码语言:javascript复制CREATE DATABASE `test`;
USE `test`
CREATE TABLE `user`(
`id` VARCHAR(50) NOT NULL,
`name` VARCHAR(10) DEFAULT NULL,
`age` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
CREATE TABLE `teacher`(
`id` VARCHAR(50) NOT NULL,
`name` VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
CREATE TABLE `student`(
`id` VARCHAR(50) NOT NULL,
`name` VARCHAR(10) DEFAULT NULL,
`teacher` VARCHAR(50) DEFAULT NULL ,
PRIMARY KEY (`id`),
FOREIGN KEY(`teacher`) REFERENCES teacher(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8
create table `book`(
`id` varchar(50) primary key,
`name` varchar(20) default null,
`money` double default 0.0,
`buy_date` datetime
)engine=InnoDB default charset=utf8
INSERT INTO `user`(`id`,`name`,`age`) VALUES
(0,"ZWZ01","123456"),
(1,"ZWZ02","123456"),
(2,"ZWZ03","123456");
INSERT INTO `teacher`(`id`,`name`) VALUES (1,'ZWZ');
INSERT INTO `student`(`id`,`name`,`teacher`) VALUES (1,'ZWZ01',1);
INSERT INTO `student`(`id`,`name`,`teacher`) VALUES (2,'ZWZ02',1);
INSERT INTO `student`(`id`,`name`,`teacher`) VALUES (3,'ZWZ03',1);
INSERT INTO `student`(`id`,`name`,`teacher`) VALUES (4,'ZWZ04',1);
INSERT INTO `student`(`id`,`name`,`teacher`) VALUES (5,'ZWZ05',1);
1.6 idea连接MySQL
设置时区后,点击 Test Connection
属性 | 值 |
---|---|
serverTimezone | Asia/Shanghai |
二、增删改查
2.1 公用类
数据库配置
db.properties,用于连接数据库的重要信息,一般为驱动名、URL地址、用户名、密码
代码语言:javascript复制driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username=root
password=123456
mybatis配置
mybatis-config.xml,mybatis的配置文件
代码语言:javascript复制<?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 resource="db.properties"/>
<settings>
<!--开启日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--驼峰转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="zwz.pojo"/>
</typeAliases>
<environments default="zwz">
<environment id="zwz">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="zwz.mapper.UserMapper"/>
</mappers>
</configuration>
实体类
用户类User,包含id、name、age三个变量,其中Id由工具类 IdUtils 自动生成
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String id;
private String name;
private int age;
}
SqlSession工具类
- 用于获取session对象
- 先从 SqlSessionFactoryBuilder 工厂构造器构建SqlSessionFactory
- 再从SqlSessionFactory工厂生产SqlSession
- 为了简化代码,封装成一个工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
随机ID工具类
该类用于随机生成ID,其中横线被替换为空,也可以替换成其他值
代码语言:javascript复制public class IdUtils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
2.2 增加用户
mapper层接口
要实现增加用户,第一步先写一个接口,在用XML具体实现这个接口
代码语言:javascript复制public interface UserMapper {
// 增加用户
int addUser(User user);
}
XML具体实现
- namespace是命名空间,必须和接口的地址相对应
- id为接口方法名,必须对应
- parameterType为传入参数类型,因为已配置别名,一般使用首字母小写的类名
- #{}中的字符串,要和实体类变量名对应,或用resultMap
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.UserMapper">
<insert id="addUser" parameterType="user">
insert into user(id, name, age) VALUES (#{id},#{name},#{age});
</insert>
</mapper>
Insert, Update, Delete 元素的属性如下
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
测试
- 测试方法中,增加了六个用户
- 增删改操作之后必须提交事务,否则无效
/**
* 增加用户
*/
@Test
public void testAddUser(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.addUser(new User(IdUtils.getId(),"ZWZ1",18));
mapper.addUser(new User(IdUtils.getId(),"ZWZ2",18));
mapper.addUser(new User(IdUtils.getId(),"ZWZ3",18));
mapper.addUser(new User(IdUtils.getId(),"ZWZ4",18));
mapper.addUser(new User(IdUtils.getId(),"ZWZ5",18));
mapper.addUser(new User(IdUtils.getId(),"ZWZ6",18));
session.commit();
session.close();
}
日志输出
可以看到,系统执行了六次insert SQL语句,成功插入
代码语言:javascript复制Created connection 1552326679.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c86a017]
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String), ZWZ1(String), 18(Integer)
<== Updates: 1
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: 7322170480104981a58b28f51fdb2288(String), ZWZ2(String), 18(Integer)
<== Updates: 1
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: f0d102cd857645c9aaca4ded53abb0e8(String), ZWZ3(String), 18(Integer)
<== Updates: 1
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: e4993aecfd4f4322905eb6dc0cf039b9(String), ZWZ4(String), 18(Integer)
<== Updates: 1
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: 4e387cafc50840468c4dfeec54940e1c(String), ZWZ5(String), 18(Integer)
<== Updates: 1
==> Preparing: insert into user(id, name, age) VALUES (?,?,?);
==> Parameters: 35ee11b46afa47a68be7713ecdeccd7e(String), ZWZ6(String), 18(Integer)
<== Updates: 1
Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c86a017]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c86a017]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5c86a017]
Returned connection 1552326679 to pool.
Process finished with exit code 0
2.3 删除用户
mapper层接口
删除、修改、查询原理同增加用户
代码语言:javascript复制// 删除用户
int deleteUser(@Param("id") String id);
XML具体实现
代码语言:javascript复制<delete id="deleteUser" parameterType="string">
delete from user where id = #{id};
</delete>
测试
其中删除方法deleteUser中的参数ID,根据实际情况修改
代码语言:javascript复制/**
* 删除用户
*/
@Test
public void testDeleteUser(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 根据数据库用户ID自行填写
mapper.deleteUser("35ee11b46afa47a68be7713ecdeccd7e");
session.commit();
session.close();
}
日志输出
可以看出,系统执行了一条delete语句,用户成功被删除
代码语言:javascript复制Created connection 1473981203.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@57db2b13]
==> Preparing: delete from user where id = ?;
==> Parameters: 35ee11b46afa47a68be7713ecdeccd7e(String)
<== Updates: 1
Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@57db2b13]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@57db2b13]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@57db2b13]
Returned connection 1473981203 to pool.
Process finished with exit code 0
2.4 修改用户
mapper层接口
代码语言:javascript复制// 指定ID查询用户
User findUserById(@Param("id") String id);
XML具体实现
代码语言:javascript复制<update id="updateUser" parameterType="user">
update user set name = #{name}, age = #{age} where id = #{id};
</update>
测试
代码语言:javascript复制/**
* 修改用户
*/
@Test
public void testUpdateUser(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 根据数据库用户ID自行填写
mapper.updateUser(new User("9e3eed6af6da4214b520535cac13f13a","ZWZ1改",18));
session.commit();
session.close();
}
日志输出
代码语言:javascript复制Created connection 454884231.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b1cfb87]
==> Preparing: update user set name = ?, age = ? where id = ?;
==> Parameters: ZWZ1改(String), 18(Integer), 9e3eed6af6da4214b520535cac13f13a(String)
<== Updates: 1
Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b1cfb87]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b1cfb87]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b1cfb87]
Returned connection 454884231 to pool.
Process finished with exit code 0
2.5 单个查询
mapper层接口
其中传递非引用类型参数,建议加上@Param注解,注解中的字符串和XML文件#{}中对应
代码语言:javascript复制// 指定ID查询用户
User findUserById(@Param("id") String id);
XML具体实现
代码语言:javascript复制<select id="findUserById" resultType="user" parameterType="string">
select * from user where id = #{id};
</select>
Select 元素的属性如下
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache | 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
fetchSize | 这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。 |
statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false。 |
resultSets | 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
测试
代码语言:javascript复制/**
* 查询单个用户
*/
@Test
public void testFindUserById(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.findUserById("9e3eed6af6da4214b520535cac13f13a");
System.out.println(user);
}
日志输出
可以看出系统执行了一条查询语句
代码语言:javascript复制Created connection 2113748097.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7dfd3c81]
==> Preparing: select * from user where id = ?;
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Total: 1
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
Process finished with exit code 0
2.6 查询全部
mapper层接口
代码语言:javascript复制<select id="findAllUsers" resultType="user">
select * from user;
</select>
XML具体实现
代码语言:javascript复制// 查询全部用户
List<User> findAllUsers();
测试
代码语言:javascript复制@Test
public void testFindAllUsers(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.findAllUsers();
for (User user : users) {
System.out.println(user);
}
}
日志输出
代码语言:javascript复制Created connection 352367347.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1500b2f3]
==> Preparing: select * from user;
==> Parameters:
<== Columns: id, name, age
<== Row: 4e387cafc50840468c4dfeec54940e1c, ZWZ5, 18
<== Row: 7322170480104981a58b28f51fdb2288, ZWZ2, 18
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Row: e4993aecfd4f4322905eb6dc0cf039b9, ZWZ4, 18
<== Row: f0d102cd857645c9aaca4ded53abb0e8, ZWZ3, 18
<== Total: 5
User(id=4e387cafc50840468c4dfeec54940e1c, name=ZWZ5, age=18)
User(id=7322170480104981a58b28f51fdb2288, name=ZWZ2, age=18)
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
User(id=e4993aecfd4f4322905eb6dc0cf039b9, name=ZWZ4, age=18)
User(id=f0d102cd857645c9aaca4ded53abb0e8, name=ZWZ3, age=18)
Process finished with exit code 0
三、resultMap结果集映射
- resultMap 是 MyBatis 中最重要最强大的元素。
- 它可以进行一些 JDBC 不支持的操作。
- 设计思想,对简单的语句做到零配置,对于复杂语句,只需要描述语句之间的关系
需要实现的功能
使用resultMap ,查询所有的User
mapper接口
mapper接口和之前一样,约束需要实现的功能
代码语言:javascript复制public interface UserMapper {
List<User> findAllUsers();
}
XML具体实现
- findAllUsers 方法配置了 resultMap
- userMap配置了数据库列和Java实体类的关系
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.UserMapper">
<select id="findAllUsers" resultMap="userMap">
select * from user;
</select>
<resultMap id="userMap" type="user">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
</mapper>
测试
代码语言:javascript复制@Test
public void testResultMap(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.findAllUsers();
for (User user : users) {
System.out.println(user);
}
}
日志输出
可以看到和之前一样,也实现了查询操作
代码语言:javascript复制Created connection 126189538.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7857fe2]
==> Preparing: select * from user;
==> Parameters:
<== Columns: id, name, age
<== Row: 4e387cafc50840468c4dfeec54940e1c, ZWZ5, 18
<== Row: 7322170480104981a58b28f51fdb2288, ZWZ2, 18
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Row: e4993aecfd4f4322905eb6dc0cf039b9, ZWZ4, 18
<== Row: f0d102cd857645c9aaca4ded53abb0e8, ZWZ3, 18
<== Total: 5
User(id=4e387cafc50840468c4dfeec54940e1c, name=ZWZ5, age=18)
User(id=7322170480104981a58b28f51fdb2288, name=ZWZ2, age=18)
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
User(id=e4993aecfd4f4322905eb6dc0cf039b9, name=ZWZ4, age=18)
User(id=f0d102cd857645c9aaca4ded53abb0e8, name=ZWZ3, age=18)
Process finished with exit code 0
四、分页
4.1 XML版 limit
mapper层接口
代码语言:javascript复制public interface UserMapper {
// 使用Limit语句分页
List<User> findAllByPageLimit(@Param("startPage") int startPage,@Param("pageSize") int pageSize);
}
XML具体实现
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.UserMapper">
<select id="findAllByPageLimit" resultType="user">
select * from user limit #{startPage},#{pageSize};
</select>
</mapper>
测试
下标从0开始,所以是输出第三个元素开始,输出三个用户
代码语言:javascript复制@Test
public void testFindAllByPageLimit(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.findAllByPageLimit(2, 3);
for (User user : users) {
System.out.println(user);
}
}
日志输出
代码语言:javascript复制Created connection 1041109062.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3e0e1046]
==> Preparing: select * from user limit ?,?;
==> Parameters: 2(Integer), 3(Integer)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Row: e4993aecfd4f4322905eb6dc0cf039b9, ZWZ4, 18
<== Row: f0d102cd857645c9aaca4ded53abb0e8, ZWZ3, 18
<== Total: 3
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
User(id=e4993aecfd4f4322905eb6dc0cf039b9, name=ZWZ4, age=18)
User(id=f0d102cd857645c9aaca4ded53abb0e8, name=ZWZ3, age=18)
Process finished with exit code 0
4.2 Java注解版 limit
XML可以实现分页,Java直接也可以实现分页
mapper接口
代码语言:javascript复制public interface UserMapper {
// 使用Java注解分页
@Select("select * from user limit #{startPage},#{pageSize};")
List<User> findAllByJava(@Param("startPage") int startPage,@Param("pageSize") int pageSize);
}
测试
代码语言:javascript复制@Test
public void testFindAllByJava(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.findAllByJava(2, 3);
for (User user : users) {
System.out.println(user);
}
}
日志输出
代码语言:javascript复制Created connection 1825719826.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6cd24612]
==> Preparing: select * from user limit ?,?;
==> Parameters: 2(Integer), 3(Integer)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Row: e4993aecfd4f4322905eb6dc0cf039b9, ZWZ4, 18
<== Row: f0d102cd857645c9aaca4ded53abb0e8, ZWZ3, 18
<== Total: 3
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
User(id=e4993aecfd4f4322905eb6dc0cf039b9, name=ZWZ4, age=18)
User(id=f0d102cd857645c9aaca4ded53abb0e8, name=ZWZ3, age=18)
Process finished with exit code 0
本文原创首发CSDN,本文链接https://blog.csdn.net/qq_41464123/article/details/108172276作者博客https://blog.csdn.net/qq_41464123,转载请带上本链接,谢谢配合。
五、多对一
需求
- 对于学生而言,都有一位老师
- 对于老师而言,有多名学生
- 在学生角度看,学生和老师就是多对一的关系
- 要求输出学生的所有信息,包括他的老师
新实体类
学生
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String id;
private String name;
private Teacher teacher;
}
老师
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private String id;
private String name;
}
方法一
先查询学生信息,再根据老师ID,查询对应老师信息
mapper接口
代码语言:javascript复制public interface StudentMapper {
/**
* 查询学生所有信息,包括其对应的老师 方法1
* @return
*/
List<Student> findAllStudentMessages1();
}
XML具体实现
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.StudentMapper">
<!--需求: 查询学生所有信息,包括其对应的老师-->
<!-- 1. 查询所有学生,设置resultMap -->
<select id="findAllStudentMessages1" resultMap="teacherAndStudent1">
select * from student;
</select>
<!-- 2. 关联映射, association为对象标签 -->
<resultMap id="teacherAndStudent1" type="student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<!-- property:在Student类中的变量名 | column:数据库的字段 | javaType:Java对象 | select:数据来源 -->
<association property="teacher" column="teacher" javaType="zwz.pojo.Teacher" select="findTeacherById"/>
</resultMap>
<!-- 3. 补充第二步的数据来源 -->
<select id="findTeacherById" resultType="teacher">
select * from teacher
where id = #{teacher};
</select>
</mapper>
测试
代码语言:javascript复制@Test
public void testFindAllStudentMessages1(){
SqlSession session = MybatisUtils.getSqlSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudentMessages1();
for (Student student : students) {
System.out.println(student);
}
}
日志输出
可以看出,系统先执行 select * from student,然后根据查询到的老师ID,再执行 select * from teacher where id = ?
代码语言:javascript复制Created connection 1651162064.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@626abbd0]
==> Preparing: select * from student;
==> Parameters:
<== Columns: id, name, teacher
<== Row: 5992b89d9cfd4f0ba79a2991e108d017, ZWZ5, 5992b89d9cfd4f0ba79a2991e108d0b7
====> Preparing: select * from teacher where id = ?;
====> Parameters: 5992b89d9cfd4f0ba79a2991e108d0b7(String)
<==== Columns: id, name
<==== Row: 5992b89d9cfd4f0ba79a2991e108d0b7, 郑老师
<==== Total: 1
<== Row: 5992b89d9cfd4f0ba79a2991e108d1b7, ZWZ1, 5992b89d9cfd4f0ba79a2991e108d0b7
<== Total: 2
Student(id=5992b89d9cfd4f0ba79a2991e108d017, name=ZWZ5, teacher=Teacher(id=5992b89d9cfd4f0ba79a2991e108d0b7, name=郑老师))
Student(id=5992b89d9cfd4f0ba79a2991e108d1b7, name=ZWZ1, teacher=Teacher(id=5992b89d9cfd4f0ba79a2991e108d0b7, name=郑老师))
Process finished with exit code 0
方法二
使用SQL语句做关联查询,再匹配到对应字段
mapper接口
代码语言:javascript复制public interface StudentMapper {
/**
* 查询学生所有信息,包括其对应的老师 方法2
* @return
*/
List<Student> findAllStudentMessages2();
}
XML具体实现
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.StudentMapper">
<!--需求: 查询学生所有信息,包括其对应的老师-->
<!-- 1. 查询所有学生和老师的关联结果,设置resultMap -->
<select id="findAllStudentMessages2" resultMap="teacherAndStudent2">
select t.id tid,t.name tname,s.id,s.name,s.teacher
from teacher t,student s
where s.teacher = t.id;
</select>
<!-- 2. 分别对应-->
<resultMap id="teacherAndStudent2" type="student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<association property="teacher" javaType="teacher">
<result column="tid" property="id"/>
<result column="tname" property="name"/>
</association>
</resultMap>
</mapper>
测试
代码语言:javascript复制/**
* 方法二
*/
@Test
public void testFindAllStudentMessages2(){
SqlSession session = MybatisUtils.getSqlSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudentMessages2();
for (Student student : students) {
System.out.println(student);
}
}
日志输出
可以看出,系统直接执行 select t.id tid,t.name tname,s.id,s.name,s.teacher from teacher t,student s where s.teacher = t.id;
代码语言:javascript复制Created connection 1304589447.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4dc27487]
==> Preparing: select t.id tid,t.name tname,s.id,s.name,s.teacher from teacher t,student s where s.teacher = t.id;
==> Parameters:
<== Columns: tid, tname, id, name, teacher
<== Row: 5992b89d9cfd4f0ba79a2991e108d0b7, 郑老师, 5992b89d9cfd4f0ba79a2991e108d017, ZWZ5, 5992b89d9cfd4f0ba79a2991e108d0b7
<== Row: 5992b89d9cfd4f0ba79a2991e108d0b7, 郑老师, 5992b89d9cfd4f0ba79a2991e108d1b7, ZWZ1, 5992b89d9cfd4f0ba79a2991e108d0b7
<== Total: 2
Student(id=5992b89d9cfd4f0ba79a2991e108d017, name=ZWZ5, teacher=Teacher(id=5992b89d9cfd4f0ba79a2991e108d0b7, name=郑老师))
Student(id=5992b89d9cfd4f0ba79a2991e108d1b7, name=ZWZ1, teacher=Teacher(id=5992b89d9cfd4f0ba79a2991e108d0b7, name=郑老师))
Process finished with exit code 0
六、一对多
需求
- 对于学生而言,都有一位老师
- 对于老师而言,有多名学生
- 在老师角度看,老师和学生就是一对多的关系
- 要求输出老师的所有信息,包括他的所有学生
实体类
学生
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String id;
private String name;
// 对应老师的ID即可
private String teacher;
}
老师
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private String id;
private String name;
// 老师拥有的学生列表
private List<Student> students;
}
具体实现
mapper接口
代码语言:javascript复制public interface TeacherMapper {
List<Teacher> findAllTeacherMessage();
}
XML具体实现
collection 是一个一个复杂类型的集合
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.TeacherMapper">
<select id="findAllTeacherMessage" resultMap="teacherAndStudent">
select t.id,t.name,s.id sid,s.name sname,s.teacher sTeacher
from teacher t,student s
where t.id = s.teacher;
</select>
<resultMap id="teacherAndStudent" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="teacher" column="sTeacher"/>
</collection>
</resultMap>
</mapper>
测试
代码语言:javascript复制@Test
public void testFindAllTeacherMessage(){
SqlSession session = MybatisUtils.getSqlSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.findAllTeacherMessage();
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
}
日志输出
可以看出系统直接执行 select t.id,t.name,s.id sid,s.name sname,s.teacher sTeacher from teacher t,student s where t.id = s.teacher; 查询到了结果
代码语言:javascript复制Created connection 2142565033.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7fb4f2a9]
==> Preparing: select t.id,t.name,s.id sid,s.name sname,s.teacher sTeacher from teacher t,student s where t.id = s.teacher;
==> Parameters:
<== Columns: id, name, sid, sname, sTeacher
<== Row: 5992b89d9cfd4f0ba79a2991e108d0b7, 郑老师, 5992b89d9cfd4f0ba79a2991e108d017, ZWZ5, 5992b89d9cfd4f0ba79a2991e108d0b7
<== Row: 5992b89d9cfd4f0ba79a2991e108d0b7, 郑老师, 5992b89d9cfd4f0ba79a2991e108d1b7, ZWZ1, 5992b89d9cfd4f0ba79a2991e108d0b7
<== Total: 2
Teacher(id=5992b89d9cfd4f0ba79a2991e108d0b7, name=郑老师, students=[Student(id=5992b89d9cfd4f0ba79a2991e108d017, name=ZWZ5, teacher=5992b89d9cfd4f0ba79a2991e108d0b7), Student(id=5992b89d9cfd4f0ba79a2991e108d1b7, name=ZWZ1, teacher=5992b89d9cfd4f0ba79a2991e108d0b7)])
Process finished with exit code 0
七、动态SQL
简介
- 动态 SQL 是 MyBatis 的强大特性之一
- 动态 SQL 用来拼接 SQL 语句
新实体类
Book
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
private String id;
private String name;
private double money;
private Date buyDate;
}
IF
mapper接口
代码语言:javascript复制public interface BookMapper {
/**
* if 标签测试
*
* 模拟查询书名和比某价格便宜的书
* 两个条件都看
*/
List<Book> findBookByNameAndMoney(@Param("name") String name, @Param("money") Double money);
}
XML具体实现
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.BookMapper">
<select id="findBookByNameAndMoney" resultType="book">
select * from book
where 1 = 1
<if test="name != ''">
and name like #{name}
</if>
<if test="money != 0.0">
and money < #{money}
</if>
</select>
</mapper>
测试
代码语言:javascript复制/**
* 模拟查询书名和比某价格便宜的书
* 如果name为空 忽略
* 如果价格为0.0 忽略
*/
@Test
public void testDynamicChoose(){
SqlSession session = MybatisUtils.getSqlSession();
BookMapper mapper = session.getMapper(BookMapper.class);
List<Book> books = mapper.findBookByNameOrMoney("%设计%", 80.0);
for (Book book : books) {
System.out.println(book);
}
}
Choose
代码语言:javascript复制public interface BookMapper {
/**
* choose 标签测试
*
* 模拟查询书名和比某价格便宜的书
* 如果姓名不为空,只看姓名
* 如果姓名为空,就看价格
*
* @param name
* @param money
* @return
*/
List<Book> findBookByNameOrMoney(@Param("name") String name, @Param("money") Double money);
}
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.BookMapper">
<select id="findBookByNameOrMoney" resultType="book">
select * from book
where 1 = 1
<choose>
<when test="name != ''">
and name like #{name}
</when>
<when test="money != 0.0">
and money < #{money}
</when>
<otherwise>
and 1 = 1
</otherwise>
</choose>
</select>
</mapper>
代码语言:javascript复制/**
* 模拟查询书名和比某价格便宜的书
* 如果name为空 忽略
* 如果价格为0.0 忽略
*/
@Test
public void testDynamicChoose(){
SqlSession session = MybatisUtils.getSqlSession();
BookMapper mapper = session.getMapper(BookMapper.class);
List<Book> books = mapper.findBookByNameOrMoney("%设计%", 80.0);
for (Book book : books) {
System.out.println(book);
}
}
Foreach
代码语言:javascript复制public interface BookMapper {
/**
* foreach 标签测试
*
* 查询指定书名的书列表
*
* @param books
* @return
*/
List<Book> findInName(@Param("books") List<String> books);
}
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zwz.mapper.BookMapper">
<select id="findInName" resultType="book" parameterType="list">
select * from book
where 1 = 1 and name in
<foreach collection="books" index="index" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
</mapper>
代码语言:javascript复制/**
* 查询指定书名的书列表
*/
@Test
public void testDynamicForeach(){
SqlSession session = MybatisUtils.getSqlSession();
BookMapper mapper = session.getMapper(BookMapper.class);
List<String> bookNames = new ArrayList<>();
bookNames.add("JavaScript程序设计");
bookNames.add("JAVA程序设计");
bookNames.add("C 程序设计");
List<Book> books = mapper.findInName(bookNames);
for (Book book : books) {
System.out.println(book);
}
}
八、缓存
8.1 一级缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制
默认情况下,只启用了本地的会话缓存(一级缓存),它仅仅对一个会话中的数据进行缓存
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
比如两次查询同样的用户,可以发现系统只执行了一次SQL指令
代码语言:javascript复制@Test
public void testCache1(){
SqlSession session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
// 第一次查询
User user = mapper.findUserById("7a0f791f1e7b416ea01b860470fa9aeb");
System.out.println(user);
System.out.println(" ");
// 第二次查询
User user2 = mapper.findUserById("7a0f791f1e7b416ea01b860470fa9aeb");
System.out.println(user2);
System.out.println(user == user2);
session.close();
}
输出日志如下
代码语言:javascript复制Created connection 508512860.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e4f4a5c]
==> Preparing: select * from user where id = ?;
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Total: 1
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
User(id=9e3eed6af6da4214b520535cac13f13a, name=ZWZ1改, age=18)
true
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e4f4a5c]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1e4f4a5c]
Returned connection 508512860 to pool.
Process finished with exit code 0
8.2 二级缓存
关闭二级缓存
如果没有开启二级缓存,在两个不同的SqlSession中发起查询,系统会执行两次SQL指令
代码语言:javascript复制@Test
public void testCache2(){
SqlSession session1 = MybatisUtils.getSqlSession();
UserMapper2 mapper1 = session1.getMapper(UserMapper2.class);
User2 user1 = mapper1.findUserById("9e3eed6af6da4214b520535cac13f13a");
session1.close();
SqlSession session2 = MybatisUtils.getSqlSession();
UserMapper2 mapper2 = session2.getMapper(UserMapper2.class);
User2 user2 = mapper2.findUserById("9e3eed6af6da4214b520535cac13f13a");
session2.close();
}
输出日志如下
代码语言:javascript复制Created connection 2075952726.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
==> Preparing: select * from user where id = ?;
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
Returned connection 2075952726 to pool.
Opening JDBC Connection
Checked out connection 2075952726 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
==> Preparing: select * from user where id = ?;
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7bbc8656]
Returned connection 2075952726 to pool.
Process finished with exit code 0
开启二级缓存
在mybatis-config.xml中配置全局缓存
代码语言:javascript复制<settings>
<!--开启日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--驼峰转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
在mapper层xml中,配置cache
代码语言:javascript复制<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
测试
代码语言:javascript复制@Test
public void testCache2(){
SqlSession session1 = MybatisUtils.getSqlSession();
UserMapper2 mapper1 = session1.getMapper(UserMapper2.class);
User2 user1 = mapper1.findUserById("9e3eed6af6da4214b520535cac13f13a");
session1.close();
SqlSession session2 = MybatisUtils.getSqlSession();
UserMapper2 mapper2 = session2.getMapper(UserMapper2.class);
User2 user2 = mapper2.findUserById("9e3eed6af6da4214b520535cac13f13a");
session2.close();
}
从日志输出中可以发现,系统只执行了一次SQL指令
代码语言:javascript复制Created connection 307829448.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@12591ac8]
==> Preparing: select * from user where id = ?;
==> Parameters: 9e3eed6af6da4214b520535cac13f13a(String)
<== Columns: id, name, age
<== Row: 9e3eed6af6da4214b520535cac13f13a, ZWZ1改, 18
<== Total: 1
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@12591ac8]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@12591ac8]
Returned connection 307829448 to pool.
Cache Hit Ratio [cache2.mapper.UserMapper2]: 0.5
Process finished with exit code 0