文章目录
Mybatis(一)
一. MyBatis简介
1.1 框架概念
框架是软件的半成品,完成了软件开发中的通用操作,程序员只需少量或无需加工即可实现特定功能,从而简化开发步骤,提高效率。
1.2 常用框架
- MVC框架:简化并强化了原先Servlet的开发步骤
- 持久层框架:负责数据库操作
- 胶水框架:Spring
SSM Spring SpringMVC MyBatis 技术栈
1.3 MyBatis介绍
MyBatis是一个半自动的ORM框架
ORM(Object Relational Mapping)对象关系映射,将Java中的对象与数据表中的记录对应。
ORM框架通过配置映射文件,实现对象的持久化。
- MyBatis的前身是iBatis,iBatis是Apache软件基金会的开源项目
- 2010年,iBatis迁移到Google Code,正式更名为MyBatis
- 2013年迁移到GitHub托管
二. MyBatis框架部署
框架部署就是将框架引入到项目中。
2.1 创建Maven项目
2.2 在项目中添加MyBatis依赖
- 在pom.xml中添加依赖:
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
2.3 创建MyBatis配置文件
- 创建自定义模板:选择resources - 右键New - Edit File Templates
- 在resources中创建名为db.properties的文件。
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://81.70.199.213:3306/java21?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.user=root
db.password=xxx
• 在resources中创建名为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 resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
三. MyBatis框架使用
案例:学生信息的数据库操作
3.1 创建数据表
CREATE TABLE `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`age` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
3.2 创建实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String email;
private Integer age;
}
3.3 创建Mapper接口,定义操作方法
public interface UserMapper {
User selectUserById(Long id);
int insertUser(User user);
}
3.4 创建mapper接口的映射文件
• 在resources目录下,新建名为mappers文件夹
• 在mappers中新建名为UserMapper.xml的映射文件(根据模板创建)
• 在映射文件中对mapper中定义的方法进行实现:
<?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.itszt22.mybatis.mapper.UserMapper">
<select id="selectUserById" resultType="com.itszt22.mybatis.entity.User">
select * from user where id = #{id}
</select>
<insert id="insertUser" parameterType="com.itszt22.mybatis.entity.User">
insert into user(name, email, age) values(#{name},#{email},#{age})
</insert>
</mapper>
3.5 将映射文件添加到主配置文件 重要
修改resource下的 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 resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
四. 单元测试
4.1 添加单元测依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
4.2 创建单元测试类 测试代码
import com.itszt22.mybatis.dao.UserDao;
import com.itszt22.mybatis.entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class UserDaoTest {
//盲写
@Test
public void test1() throws IOException {
//1. 读取mybatis主配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2. 加载SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3. 根据2.的对象 生成 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//4. 根据3.的对象生成 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//5. 根据4.生成Dao接口的实现类
UserDao userDao = sqlSession.getMapper(UserDao.class);
userDao.addUser(new User(null, "女大学生破防曲", "qwe@qq.com", 100));
User user = userDao.getUserById(1L);
System.out.println("user = " + user);
//6. 关闭SqlSession
sqlSession.close();
}
}
五. MyBatis的基本CRUD操作
案例:用户信息的增删查改
5.1 添加操作
<insert id="addUser" parameterType="com.itszt22.mybatis.entity.User">
insert into user(name, email, age) values(#{name},#{email},#{age})
</insert>
5.2 删除操作
<delete id="removeUser">
delete from user where id = #{myid}
</delete>
5.3 修改操作
根据用户id,修改其他字段信息
<update id="updateUser" parameterType="com.itszt22.mybatis.entity.User">
update user set name=#{name},email=#{email},age=#{age}
where id=#{id}
</update>
5.4 查询操作-查询所有
• 在UserDao接口定义操作方法
• 在User.xml中“实现”DAO中定义的方法
<select id="listAllUser" resultType="com.itszt22.mybatis.entity.User">
select * from user
</select>
5.6 查询操作-多参数查询
分页查询(参数 start , pageSize)多参数可以通过 param1 param2 根据传参的顺序写在sql语句中
• 在UserDao中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名
List<User> listUserByPage(@Param("offset") int offset1,@Param("limit") int limit1);
<select id="listUserByPage" resultType="com.itszt22.mybatis.entity.User">
select * from user
limit #{offset},#{limit}
</select>
• 在User.xml配置sql时,使用#{别名}获取到指定的参数注意如果DAO操作方法没有通过@Param指定参数别名,在SQL中也可以通过arg0,arg1…或者param1,param2,…获取参数
5.7 查询操作-查询总记录数
无论是传入参数, 还是返回结果类型
• 在UserDao接口中定义操作方法
• 在User.xml配置sql,通过resultType指定当前操作的返回类型为int
<select id="selectCount">
select count(*) from user
</select>
5.8 添加操作回填生成的主键
• User.xml的添加操作标签——insert
<!-- 1. 主键回填第一种-->
<insert id="addUser" parameterType="com.itszt22.mybatis.entity.User" useGeneratedKeys="true" keyProperty="id"> <!-- 这个是带主键回填的新增操作 -->
insert into user(name, email, age) values(#{name},#{email},#{age})
</insert>
<!-- 2. 主键回填第二种 了解-->
<!-- <insert id="addUser" parameterType="com.itszt22.mybatis.entity.User"> <!– 这个是带主键回填的新增操作 –>-->
<!-- <selectKey keyProperty="id" resultType="long">-->
<!-- select last_insert_id()-->
<!-- </selectKey>-->
<!-- insert into user(name, email, age) values(#{name},#{email},#{age})-->
<!-- </insert>-->
六. 事务管理
6.1 手动提交事务
开启自动提交事务
sqlSession = sqlSessionFactory.openSession(true);
• sqlSession.commit(); 提交事务
测试类中进行事务管理
// 注意: 需要将 openSession() 中的参数去掉,或者设为 false,以关闭自动提交;
// 这样就变成手动提交
// 会影响所有 DML 操作,如果不执行提交代码,即便操作成功,也无法写入数据库
@Test
public void test5() {
User user = new User(null, "小手一指 rap 开始", "123@qq.com", 2);
userDao.addUser(user);
// 在手动提交状态下,必须执行这句代码,才会真正写入数据库中
sqlSession.commit();
// 新增后,打印这个 user 对象,可以获取数据库中自增的 id
System.out.println("user = " + user);
}
6.2 自动提交事务
通过 SqlSessionFactory 调用 openSession 方法获取 SqlSession 对象时,可以通过参数设置事务是否自动提交:
• 如果参数设置为 true,表示自动提交事务:factory.openSession(true);
• 如果参数设置为 false,或者不设置参数,表示手动提交:factory.openSession();/factory.openSession(false);
七. MyBatis主配置文件
mybatis-config.xml 是MyBatis框架的主配置文件,主要用于配置MyBatis数据源及属性信息。
7.1 properties标签
用于设置键值对或加载属性文件:
• 在resources目录下创建db.properties文件,配置键值对如下:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/aDemo?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=root
• 在mybatis-config.xml中通过properties标签引用db.properties文件。引入后,配置environment时可直接使用db.properties的key获取对应的value。
7.2 settings标签
十六、十七章节分别讲解二级缓存与延迟加载
7.3 typeAliases标签
在 typeAliases 中设置了一个 package 的别名扫描路径,该路径下的 Java 实体类都可以拥有一个别名(即首字母小写的类名)。
7.4 plugins标签
第十章节会讲到具体的分页插件的使用。
7.5 environments标签
<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
<!-- environment 标签用于配置数据库连接信息 -->
<environment id="mysql">
<!--transactionManager标签用于配置数据库管理方式
type="JDBC" 可以进行事务的提交和回滚操作
type="MANAGED" 依赖容器完成事务管理,本身不进行事务的提交和回滚操作 -->
<transactionManager type="JDBC"></transactionManager>
<!--dataSource标签就是用来配置数据库连接信息 POOLED|UNPOOLED -->
<dataSource type="POOLED">
<property name="driver" value="${mysql_driver}"/>
<property name="url" value="${mysql_url}"/>
<property name="username" value="${mysql_username}"/>
<property name="password" value="${mysql_password}"/>
</dataSource>
</environment>
</environments>
7.6 mappers标签
加载映射配置(映射文件、DAO注解)
<mappers>
<mapper resource="mappers/User.xml"></mapper>
</mappers>
八. 映射文件 mapper.xml
映射文件是MyBatis框架中的核心配置文件,用于定义SQL操作和对象-关系映射。
8.1 MyBatis Mapper初始化
MyBatis解析XML文件,将标签配置封装到Java对象中。
8.2 mapper根标签
mapper文件充当mapper接口的"实现类"。namespace属性必须指定对应mapper接口的全限定名。
8.3 insert标签
用于声明插入操作。
常用属性:
• id:绑定对应DAO接口中的方法
• parameterType:指定方法参数类型(基本类型可省略)
主键回填:
• useGeneratedKeys:设置是否需要回填生成的主键
• keyProperty:指定主键回填到参数对象的哪个属性
• timeout:设置操作超时时间,默认无限等待
8.4 delete标签
用于声明删除操作。
8.5 update标签
用于声明更新操作。
8.6 select标签
用于声明查询操作。
• id:指定绑定的方法名
• parameterType:设置参数类型
• resultType:指定返回数据封装的对象类型
• resultMap:指定数据表到实体类的字段映射关系
• useCache:指定是否使用缓存
• timeout:设置超时时间
8.7 resultMap标签
<!-- 定义resultMap,指定数据表字段与Java类属性的对应关系 -->
<resultMap id="userMap" type="user" autoMapping="true">
<result property="name" column="tencent_name"/>
</resultMap>
8.8 cache标签
注意:第15章节将详细讲解缓存。
用于设置当前mapper的缓存属性:
8.9 sql和include标签
<!-- 定义常用SQL片段 -->
<sql id="userNeedColumn">
name, age from user
</sql>
<select id="listUserByPage" resultMap="userMap">
select
<include refid="userNeedColumn"/>
limit #{offset}, #{limit}
</select>
九. 分页插件
分页插件是一个独立于MyBatis框架的第三方插件。
9.1 添加分页插件依赖
PageHelper
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
9.2 配置插件
在MyBatis的主配置文件mybatis-config.xml中通过plugins标签进行配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
9.3 分页示例
对查询信息进行分页查询:
@Test
public void testListStudentsByPage() {
PageHelper.startPage(2, 4);
List students = studentDAO.listStudents();
PageInfo pageInfo = new PageInfo<>(students);
// pageInfo中包含了数据及分页信息
}
带条件分页:
@Test
public void listUser() {
PageHelper.startPage(2, 3);
List users = userDao.listUserByLikeName2(“琦”);
PageInfo userPageInfo = new PageInfo<>(users);
}
十. 模糊查询:#{}和${}
案例:根据昵称模糊查询会员信息
10.1 模糊查询实现
10.1.1 DAO
public interface MemberDAO {
// 根据昵称模糊查询用户信息
// 模糊查询需使用
进行
S
Q
L
拼接
/
/
使用
{}进行SQL拼接 // 使用
进行SQL拼接//使用{}时,即使只有一个参数也需要用@Param注解声明参数的key(非String对象参数除外)
public List searchMemberByNick(@Param(“keyWord”) String keyWord);
}
10.1.2 映射文件
SELECT member_id, member_nick, member_gender, member_age, member_city FROM members WHERE member_nick LIKE '%${keyWord}%'使用#{}语法的两种写法:
- SELECT * FROM members WHERE member_nick LIKE CONCAT(‘%’, #{name}, ‘%’)
- SELECT * FROM members WHERE member_nick LIKE “%”#{keyWord}“%”
10.1.3 测试
@Test
public void testSearchMemberByNick() {
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List members = memberDAO.searchMemberByNick(“花”);
for (Member m : members) {
System.out.println(m);
}
}
10.2 #{}和${}的区别
10.2.1 #{} 占位符
语法:#{字符}
MyBatis处理#{}使用PreparedStatement对象:
SELECT * FROM user WHERE id = #{userId}MyBatis创建PreparedStatement对象,执行SQL语句:
String sql = “SELECT * FROM user WHERE id = ?”;
PreparedStatement pst = conn.prepareStatement(sql);
pst.setInt(1, 1L); // 传递参数
ResultSet rs = pst.executeQuery(); // 执行SQL语句
#{}特点:
- 使用PreparedStatement对象,执行SQL语句,效率高。
- 能避免SQL注入,执行更安全。
- 常用作列值,位于等号右侧,其值与数据类型相关。
10.2.2 ${} 占位符
语法:${字符}
MyBatis执行${}占位符的SQL语句:
SELECT * FROM student WHERE id = ${studentId}
表示字符串连接,将
S
Q
L
语句的其他内容与
{} 表示字符串连接,将SQL语句的其他内容与
表示字符串连接,将SQL语句的其他内容与{}内容用字符串(+)连接:
String sql = “SELECT * FROM student WHERE id=” + “1001”;
MyBatis创建Statement对象,执行SQL语句:
Statement stmt = conn.createStatement(sql);
ResultSet rs = stmt.executeQuery();
${}特点:
- 使用Statement对象,执行SQL语句,效率较低。
- 使用字符串连接方式,存在SQL注入风险,有代码安全问题。
- 数据原样使用,不区分数据类型。
- 常用作表名或列名,通常在等号左侧。仅在能保证数据安全的情况下使用${}。
十一. MyBatis日志配置
MyBatis作为一个封装良好的ORM框架,其执行过程不易追踪。为了帮助开发人员了解MyBatis的工作流程以及每个步骤完成的任务,该框架支持log4j日志系统。通过配置MyBatis的日志设置,我们可以在运行时查看详细的日志信息。
11.1 添加日志框架依赖
我们将使用log4j作为日志框架:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
11.2 添加日志配置文件
• 在resources目录下创建名为log4j.properties的文件
• 在log4j.properties文件中配置日志输出方式
以下是日志输出级别及输出方式的配置示例:
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
#log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t 表示线程名称 %5p 日志级别 %msg日志信息
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %msg :%m%n
11.3 日志信息的级别
日志框架根据信息的重要程度将日志分为5个级别:
级别 说明
DEBUG 输出调试信息
INFO 输出提示信息
WARN 输出警告信息
ERROR 输出一般性错误信息
FATAL 输出致命性错误信息
十二. 关联映射(复杂且重要, 掌握基本写法即可)
12.1 实体关系
实体关系指数据实体之间的关联,如用户和角色、房屋和楼栋、订单和商品。实体关系分为四种:
一对一关联
实例:人和身份证、学生和学生证、用户基本信息和详情
数据表关系:
• 主键关联(用户表主键与详情主键相同时,表示匹配数据)
• 唯一外键关联
一对多关联、多对一关联
实例:
• 一对多:班级和学生、类别和商品、楼栋和房屋
• 多对一:A班学生和A班级、橱柜椅子商品和家具类别
数据表关系:
• 在"多"的一端添加外键与"一"的一端关联
多对多关联
实例:用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品
数据表关系:建立第三张关系表,添加两个外键分别与两张表主键关联
例:用户(user_id) — 用户角色表(uid,rid) — 角色(role_id)
12.2 一对一关联
实例:用户—详情
12.2.1 创建数据表
– 用户信息表
CREATE TABLE users (
user_id INT auto_increment NOT NULL,
user_name VARCHAR(100) NOT NULL,
user_pwd varchar(100) NOT NULL,
user_realname varchar(100) NOT NULL,
user_img varchar(100) NOT NULL,
CONSTRAINT users_pk PRIMARY KEY (user_id),
CONSTRAINT users_un UNIQUE KEY (user_name)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;
– 用户详情表
CREATE TABLE details (
detail_id INT auto_increment NOT NULL,
user_addr varchar(100) NOT NULL,
user_tel CHAR(11) NOT NULL,
user_desc varchar(100) NULL,
uid INT NOT NULL,
CONSTRAINT details_pk PRIMARY KEY (detail_id),
CONSTRAINT details_un UNIQUE KEY (uid),
CONSTRAINT details_FK FOREIGN KEY (uid) REFERENCES users(user_id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;
12.2.2 创建实体类
• Users
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
private int userId;
private String userName;
private String userPwd;
private String userRealname;
private String userImg;
}
Detail
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Detail {
private int detailId;
private String userAddr;
private String userTel;
private String userDesc;
private int uid;
}
12.2.3 添加操作(事务的重要性)
为这两个实体类各写一个新增方法, 并后面那个方法故意报错一下, 如果是自动提交, 发现只有一个入库成功, 对于用户来说, 这将非常困惑, 成也不是, 失败也不是
解决方案: 这两个行为本来就是一体的, 要么一起成功, 要么一起失败, 通过开启手动提交, 最后commit, 如果捕获到了异常,在catch中调用rollback清空之前的临时提交记录.
12.2.4 一对一关联查询
在查询用户的同时关联查询出与之对应的详情实体映射文件子查询
12.3 一对多关联
案例:班级(1)—学生(n)
12.3.1 创建数据表
– 创建班级信息表
create table classes(
cid int primary key auto_increment,
cname varchar(30) not null unique,
cdesc varchar(100)
);
– 创建学生信息表
create table students(
sid char(5) primary key,
sname varchar(20) not null,
sage int not null,
scid int not null
);
12.3.2 创建实体类
INSERT INTO classes (cname,cdesc) VALUES
('养老','不加班'),
('996','加班');
INSERT INTO students (sid,sname,sage,scid) VALUES
('20221','wz',100,21),
('20222','lcc',100,20),
('20223','kr',100,21);
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
private int classId;
private String className;
private String classDesc;
private List stus;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String stuId;//学号
private String stuName;
private int stuAge;
private int stucid; //学生所在班级的id
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itszt22.mybatis.dao.ClassDao">
<!-- <resultMap id="classMap" type="clazz">-->
<!-- <id property="classId" column="cid"/>-->
<!-- <result property="className" column="cname"/>-->
<!-- <result property="classDesc" column="cdesc"/>-->
<!-- <collection property="stus" ofType="student">-->
<!-- <id property="stuId" column="sid"/>-->
<!-- <result property="stuName" column="sname"/>-->
<!-- <result property="stuAge" column="sage"/>-->
<!-- <result property="stucid" column="scid"/>-->
<!-- </collection>-->
<!-- </resultMap>-->
<!-- <select id="getClazzById" resultMap="classMap">-->
<!-- select * from classes-->
<!-- inner join students on classes.cid = students.scid-->
<!-- where cid = #{cid}-->
<!-- </select>-->
</mapper>
12.3.3 关联查询
当查询一个班级的时候, 要关联查询出这个班级下的所有学生
连接查询
连接查询映射配置
–i>
Clazz对象的stus属性是一个List集合,需要使用colLection标签
<-
coLLection标签的ofType属性声明集合中元素的类型
12.4 多对一关联
实例:学生(n)—班级(1)
当查询一个学生的时候,关联查询这个学生所在的班级信息
12.4.1 创建实体类
12.4.2 关联查询
12.5 多对多关联 作业
案例:学生(m)—课程(n)
12.5.1 创建数据表
– 学生信息表(如上)
– 课程信息表
create table courses(
course_id int primary key auto_increment,
course_name varchar(50) not null
);
– 选课信息表/成绩表(学号、课程号、成绩) 中间关系表
create table grades(
sid bigint not null,
cid int not null,
score int not null
);
12.5.2 关联查询
查询学生时,同时查询学生选择的课程根据课程编号查询课程时,同时查询选择了这门课程的学生连接查询映射配置
十三. 动态SQL(重要)
MyBatis的独特特征:动态SQL,这是国外的JPA无法实现的
交友网站如珍爱网、百合网允许用户筛选心仪对象,可选条件包括性别、年龄、城市和身高。
电商平台如淘宝、京东则让顾客筛选商品,例如羽毛球拍的品牌和价格。
不同用户的筛选条件各异,导致执行的SQL语句也不尽相同:
搜索女性:SELECT * FROM members WHERE gender=‘女’
搜索18-23岁女性:SELECT * FROM members WHERE gender=‘女’ AND age >= 18 AND age <= 23
按年龄和城市搜索:SELECT * FROM members WHERE age >= 18 AND age <= 23 AND city=‘’
尽管我们可以穷举所有可能的条件组合来编写SQL,但这种方法既繁琐又复杂。
MyBatis通过提供动态SQL配置,优雅地解决了多条件查询的难题。
13.1 什么是动态SQL?
根据查询条件动态完成SQL的拼接
13.2 动态SQL使用案例
案例:心仪对象搜索
13.2.1 创建数据表
create table members(
member_id int primary key auto_increment,
member_nick varchar(20) not null unique,
member_gender char(2) not null,
member_age int not null,
member_city varchar(30) not null
);
13.2.2 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
private int memberId;
private String memberNick;
private String memberGender;
private int memberAge;
private String memberCity;
}
13.2.3 创建DAO接口
在DAO接口中定义一个多条件查询的方法
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface MemberMapper {
List<Member> listMember(@Param("age") Integer age,@Param("city") String city);
void updateMember(Member member);
}
13.3 if
<!-- 1. if写法, 解决不了 一些特殊符号多出的问题 -->
<!-- <if test="age != null">-->
<!-- and member_age=#{age}-->
<!-- </if>-->
<!-- <if test="city != null">-->
<!-- and member_city=#{city}-->
<!-- </if>-->
测试
@Test
public void testSearchMember() {
HashMap<String,Object> params = new HashMap<String, Object>();
params.put("gender","女");
params.put("minAge",18);
//params.put("maxAge",23);
params.put("city","武汉");
//-----------------------------------------------------------------------
MemberSearchCondition params2 = new MemberSearchCondition();
params2.setGender("女");
//params2.setMinAge(21);
//params2.setMaxAge(30);
//params2.setCity("武汉");
//==========================================================================
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMember(params2);
for (Member m: members) {
System.out.println(m);
}
}
13.4 set
<update id="updateMember" parameterType="member">
update members
<set>
<if test="memberGender != null">
member_gender=#{memberGender},
</if>
<if test="memberAge != null">
member_age=#{memberAge},
</if>
<if test="memberNick != null">
member_nick=#{memberNick},
</if>
<if test="memberCity != null">
member_city=#{memberCity}
</if>
</set>
where member_id=#{memberId}
</update>
13.5 where
<select id="listMember" resultMap="memberMap">
select * from members
<!-- 1. if写法, 解决不了 一些特殊符号多出的问题 -->
<!-- <if test="age != null">-->
<!-- member_age=#{age} and-->
<!-- </if>-->
<!-- <if test="city != null">-->
<!-- member_city=#{city}-->
<!-- </if>-->
<!-- 2. where写法 自动发现有and or等连接词汇的, 如果多余, 自动去除-->
<where>
<if test="age != null">
and member_age=#{age}
</if>
<if test="city != null">
and member_city=#{city}
</if>
</where>
</select>
13.6 foreach
当一次性批量插入的数据超过10w条的时候, 应该使用jdbc原始的addBatch语法
如果一次性批量插入数据在10w以下的时候, 使用哪个都ok
insert into tableA values(d1, d2), (d11, d22), (d111, d222)
public interface MemberDAO {
//查询指定城市的会员
public List<Member> searchMemberByCity(List<String> cities);
}
<select id="searchMemberByCity" resultMap="memberMap">
select member_id,member_nick,member_gender,member_age,member_city
from members where member_city in
<foreach collection="list" item="cityName" separator="," open="(" close=")">
#{cityName}
</foreach>
</select>
测试
@Test
public void searchMemberByCity() {
List<String> cities = new ArrayList<String>();
cities.add("厦门");
cities.add("宜昌");
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List<Member> members = memberDAO.searchMemberByCity(cities);
for (Member m: members) {
System.out.println(m);
}
}
十四. 配置数据库连接池 - 整合Druid
MyBatis作为一个ORM框架,在进行数据库操作时需要与数据库建立连接。MyBatis支持基于数据库连接池的连接创建方式。
当我们配置MyBatis数据源时,只要将dataSource标签的type属性值设为POOLED,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需进行自定义配置。
14.1 常见的连接池
• DBCP
• C3P0
• Druid:性能较好,提供便捷的监控系统
• HikariCP:性能最佳
功能比较:
• PSCache支持:DBCP、Druid、C3P0均支持
• 监控:DBCP (JMX)、Druid (JMX/log/http)、C3P0 (JMX,log)、HikariCP (JMX)
• 扩展性:Druid好,其他较弱
• SQL拦截及解析:仅Druid支持
• 代码复杂度:DBCP简单,Druid中等,C3P0复杂,HikariCP简单
• 特点:
- DBCP:依赖于commons-pool
- Druid:阿里开源,功能全面
- C3P0:历史悠久,代码逻辑复杂,维护困难
- HikariCP:优化力度大,功能简单,源于BoneCP
• 连接池管理: - DBCP:LinkedBlockingDeque
- Druid:数组
- HikariCP:threadlocal + CopyOnWriteArrayList
14.2 添加Druid依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
14.3 创建Druid连接池工厂
public class DruidDataSourceFactory extends PooledDataSourceFactory {
public DruidDataSourceFactory() {
this.dataSource = new DruidDataSource();
}
}
14.4 将DruidDataSourceFactory配置给MyBatis数据源
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="com.itszt22.mybatis.DruidDataSourceFactory">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
十五. MyBatis的注解开发(仅供了解)
15.1 MyBatis的常用注解
近年来,注解开发日益流行。MyBatis也支持注解开发方式,这可以减少Mapper映射文件的编写。我们将先学习基本的CRUD操作,然后深入复杂的多表映射操作。
常用注解:
• @Insert:实现新增
• @Update:实现更新
• @Delete:实现删除
• @Select:实现查询
• @Result:实现结果集封装
• @Results:与@Result配合使用,封装多个结果集
• @One:实现一对一结果集封装
• @Many:实现一对多结果集封装
15.2 MyBatis的增删改查
我们将完成user表的基本增删改查操作。
修改MyBatis的核心配置文件。由于我们使用注解替代了映射文件,只需加载使用了注解的Mapper接口:
或者指定扫描包含映射关系的接口所在的包:
注意:package扫包用于通过注解实现接口,而mapper是通过XML实现接口。接口只能有一种实现方式,如果两者都存在,会报错:“重复注册,already registered”。
15.3 MyBatis的注解实现复杂映射开发
在实现复杂关系映射时,我们之前在映射文件中使用配置。使用注解开发后,我们可以组合@Results、@Result、@One和@Many注解来完成复杂关系的配置。
15.4 一对一查询
15.4.1 一对一查询的模型
用户表和订单表的关系:一个用户有多个订单,一个订单只属于一个用户。
一对一查询需求:查询一个订单,同时查询出该订单所属的用户。
15.4.2 一对一查询的语句
对应的SQL语句:
select * from orders;
select * from user where id = 查询出订单的uid;
15.4.3 创建Order和User实体
public class Order {
private int id;
private Date ordertime;
private double total;
// 代表当前订单所属的客户
private User user;
}
public class User {
private int id;
private String username;
private String password;
private Date birthday;
}
15.4.4 创建OrderMapper接口
public interface OrderMapper {
List findAll();
}
15.4.5 使用注解配置Mapper
public interface OrderMapper {
@Select(“select * from orders”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “ordertime”, column = “ordertime”),
@Result(property = “total”, column = “total”),
@Result(property = “user”, column = “uid”,
javaType = User.class,
one = @One(select = “com.itszt.mapper.UserMapper.findById”))
})
List findAll();
}
public interface UserMapper {
@Select(“select * from user where id = #{id}”)
User findById(int id);
}
15.4.6 测试结果
@Test
public void testSelectOrderAndUser() {
List all = orderMapper.findAll();
for (Order order : all) {
System.out.println(order);
}
}
15.5 一对多查询
15.5.1 一对多查询的模型
用户表和订单表的关系:一个用户有多个订单,一个订单只属于一个用户。
一对多查询需求:查询一个用户,同时查询出该用户拥有的所有订单。
15.5.2 一对多查询的语句
对应的SQL语句:
select * from user;
select * from orders where uid = 查询出用户的id;
15.5.3 修改User实体
public class Order {
private int id;
private Date ordertime;
private double total;
// 代表当前订单所属的客户
private User user;
}
public class User {
private int id;
private String username;
private String password;
private Date birthday;
// 代表当前用户拥有的订单
private List orderList;
}
15.5.4 创建UserMapper接口
List findAllUserAndOrder();
15.5.5 使用注解配置Mapper
public interface UserMapper {
@Select(“select * from user”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “username”, column = “username”),
@Result(property = “password”, column = “password”),
@Result(property = “birthday”, column = “birthday”),
@Result(property = “orderList”, column = “id”,
javaType = List.class,
many = @Many(select = “com.itszt.mapper.OrderMapper.findByUid”))
})
List findAllUserAndOrder();
}
public interface OrderMapper {
@Select(“select * from orders where uid = #{uid}”)
List findByUid(int uid);
}
15.5.6 测试结果
List all = userMapper.findAllUserAndOrder();
for (User user : all) {
System.out.println(user.getUsername());
List orderList = user.getOrderList();
for (Order order : orderList) {
System.out.println(order);
}
System.out.println(“-----------------------------”);
}
15.6 多对多查询
15.6.1 多对多查询的模型
用户表和角色表的关系:一个用户有多个角色,一个角色被多个用户使用。
多对多查询需求:查询用户同时查询出该用户的所有角色。
15.6.2 多对多查询的语句
对应的SQL语句:
select * from user;
select * from role r, user_role ur where r.id = ur.role_id and ur.user_id = 用户的id
15.6.3 创建Role实体,修改User实体
public class User {
private int id;
private String username;
private String password;
private Date birthday;
// 代表当前用户拥有的订单
private List orderList;
// 代表当前用户拥有的角色
private List roleList;
}
public class Role {
private int id;
private String rolename;
}
15.6.4 添加UserMapper接口方法
List findAllUserAndRole();
15.6.5 使用注解配置Mapper
public interface UserMapper {
@Select(“select * from user”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “username”, column = “username”),
@Result(property = “password”, column = “password”),
@Result(property = “birthday”, column = “birthday”),
@Result(property = “roleList”, column = “id”,
javaType = List.class,
many = @Many(select = “com.itszt.mapper.RoleMapper.findByUid”))
})
List findAllUserAndRole();
}
public interface RoleMapper {
@Select(“select * from role r, user_role ur where r.id = ur.role_id and ur.user_id = #{uid}”)
List findByUid(int uid);
}
15.6.6 测试结果
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List all = mapper.findAllUserAndRole();
for (User user : all) {
System.out.println(user.getUsername());
List roleList = user.getRoleList();
for (Role role : roleList) {
System.out.println(role);
}
System.out.println(“----------------------------------”);
}
动态SQL注解示例
@Select("”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “name”, column = “name”),
@Result(property = “email”, column = “email”),
@Result(property = “password”, column = “password”)
})
List findUsersByCondition(String email, String name);
十六. 总结
- MyBatis是一个半自动的ORM框架,简化了Java对象与数据库表之间的映射
- 框架部署包括添加依赖、创建配置文件和映射文件
- MyBatis支持基本的CRUD操作,可以通过XML或注解方式实现
- 动态SQL允许根据不同条件动态生成SQL语句,提高了查询的灵活性
- MyBatis支持一对一、一对多、多对多等复杂关系映射
- 可以集成第三方连接池如Druid来优化数据库连接管理
- MyBatis提供了缓存机制和延迟加载功能,可以提高查询性能
- 注解开发方式可以减少XML配置,但对于复杂查询,XML配置方式更加灵活