1. Mybatis
MyBatis 是一个开源的Java持久层框架,用于将对象与关系数据库的表之间进行映射。MyBatis 通过 XML或注解配置文件描述 Java 对象与数据库之间的映射关系,并提供了一些方便的查询语言(类似于SQL)来进行数据库操作。
使用 MyBatis 来操作 MySQL 数据库,将数据存储在 MySQL 中,或从 MySQL 中检索数据,同时使用 MyBatis 进行数据映射和数据库操作的管理。它们通常一起使用,以构建 Java 应用程序的持久层。
数据映射:
MyBatis 的核心功能之一是提供简单且强大的数据映射。使用 XML或注解来定义 SQL查询和映射结果,将数据库表记录映射到 Java对象。
- 1.1 XML 映射文件:
1
2
3
4
5
6
7
8
9
10<!-- 定义查询 -->
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 映射结果到对象 -->
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
</resultMap> - 1.2 注解方式
1
2
User selectUser(int id);
核心功能:
- 动态 SQL:MyBatis 允许你在 XML 中编写动态 SQL 语句,可以根据条件动态构建 SQL 查询。
1
2
3
4
5
6
7
8
9
10<select id="selectUsers" parameterType="map" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="username != null">
AND username = #{username}
</if>
<if test="password != null">
AND password = #{password}
</if>
</select> - 参数传递:MyBatis 支持多种参数传递方式,包括单个参数、多个参数、Map 和注解等。
1
2
User selectUserByIdAndUsername(int id, String username); - 批处理:MyBatis 允许执行批处理操作,可以有效地执行一组 SQL 语句。
1
2
3
4
5
6
7
8SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
for (User user : userList) {
userMapper.insertUser(user);
}
sqlSession.flushStatements();
sqlSession.commit();
sqlSession.close();
事务管理:
- MyBatis 也提供了事务管理的支持。可以通过配置数据源和事务管理器来实现事务的控制。
1
2
3
4
5
6
7
8
9<!-- 数据源配置 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
<!-- 事务管理器配置 -->
<transactionManager type="JDBC"/> - MyBatis 可以很容易地与 Spring 框架集成,通过 Spring 的事务管理来控制数据库事务。
1
2
3
4
5
6
7
8<!-- Spring 配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
SpringBoot实体类 —— VO/DTO/PO
VO:View Object,主要用于展示层。它的作用是把某个指定前端页面的所有数据封装起来。他的作用主要是减少传输数据量大小和保护数据库隐私数据(如用户密码、用户邮箱等相关信息)不外泄,同时保护数据库的结构不外泄。
DTO:Data Transfer Object,数据传输对象,用于展示层与服务层之间的数据传输对象。(注:实际开发中还存在BO,其作用和DTO类似,当业务逻辑不复杂时一般会被合并。)
PO:Persistant Object,持久化对象,和数据库形成映射关系。简单说PO就是每一个数据库中的数据表,一个字段对应PO中的一个变量。(也就是我们常用的Entities)
1、从前端页面中收到JSON格式数据,后端接口中将其封装为一个VO对象;接口接收到VO对象后将其转换为DTO对象,并调用业务类方法对其进行处理;然后处理为PO对象,调用Dao接口连接数据库进行数据访问(查询、插入、更新等)2、后端从数据库得到结果后,根据Dao接口将结果映射为PO对象,然后调用业务类方法将其转换为需要的DTO对象,再根据前端页面实际需求,转换为VO对象进行返回。
类型转换:上述过程中,VO/DTO/PO等实体类中字段常常会存在多数相同,根据业务需求少数不同。为避免频繁的set和get操作对其进行转换,spring为我们提供了多种方法。(1)使用BeanUtils:(springframework包下)(2)使用BeanUtils:(Apache包下)(3)使用modelMapper??
DO(Data Object):通常表示数据库中的数据实体,对应数据库表的结构。它主要用于数据存储和数据库操作,包含与数据库表字段一一对应的属性。类中通常包含与数据库表字段对应的成员变量、getter 和 setter 方法。它不应包含业务逻辑,主要负责数据的持久化和映射。
尽管 PO 和 DO 在一些情况下用法相似,但它们的侧重点有所不同。PO 更侧重于与数据库的交互,强调持久化和数据表映射;而 DO 侧重于在不同层之间传递数据,强调业务逻辑层面的数据封装。BO(Business Object):通常表示业务层的业务实体,主要用于封装业务逻辑。BO 类一般包含与业务逻辑相关的属性和方法,与具体的数据存储形式无关。包含了一些业务逻辑的操作,比如计算、验证等。它不应直接与数据库进行交互,而是通过调用 Service 层或 DAO 层的方法实现数据的获取和存储。
2. Mybatis-Plus
- 基于MyBatis:MyBatis-Plus是MyBatis的增强工具包,是在MyBatis基础上的扩展。只做增强不做改变,为简化开发、提高效率而生。它提供了更多的便捷、高效的开发功能,简化了开发人员的编码工作,大幅度提高了开发效率。
- 功能:MyBatis-Plus 集成了MyBatis的核心功能,同时提供了更多针对CRUD操作、条件构造器、分页、代码生成器等功能的封装。
- 简化操作:可以减少重复的CRUD代码,提供了一些便捷的API接口和工具,使得开发人员能够更方便地进行数据库操作。
- 引入 MybatisPlus依赖,可以直接代替 Mybatis依赖
1
2
3
4
5<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency> - MyBatisPlus 的配置项继承了 MyBatis原生配置和一些自己特有的配置。例如:
1
2
3
4
5
6
7
8
9
10mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
mapper-locations:"classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
configuration:
map-underscore-to-camel-case: true # 是否开户下划线和驼峰的映射
cache-enabled: false # 是否开户二级缓存
global-config:
db-config:
id-type: assign_id # id为雪花算法生成
update-strategy: not_null # 更新笑略:只更新非空字段
BaseMapper
- 定义 Mapper接口并继承
BaseMapper
类,泛型指定要与数据库映射的 Java实体类(pojo类);
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息,自动实现 CRUD的逻辑- 默认以类名驼峰转下划线作为表名(User类 -> user表)
- 默认把名为id的字段作为主键
- 默认把变量名驼峰转下划线作为表的字段名(createTime类属性 -> create_time表字段)
1
public interface UserMapper extends BaseMapper<User>{}
- 如果实体类和表的对应关系不符合 mp的约定,就要自行配置。可以使用注解:
@TableName
:指定表名称及全局配置@Tableld
:指定id字段及相关配置;- IdType的常见类型有:AUTO、ASSIGN ID(默认使用,雪花算法)、INPUT
@TableField
:指定普通字段及相关配置。使用 @TableField的常见场景是:- 1、成员变量名与数据库字段名不一致 2、成员变量名以is开头,且是布尔值
3、成员变量名与数据库关键字冲突 4、成员变量不是数据库字段
- 1、成员变量名与数据库字段名不一致 2、成员变量名以is开头,且是布尔值
条件构造器
MyBatisPlus支持 使用 Wrapper构造各种复杂的where条件,而不需要在 xml中写sql语句。可以满足日常开发的所有需求。
- QueryWrapper 和LambdaQueryWrapper通常用来构建 select、delete、update的 where条件部分
- UpdateWrapper 和LambdaUpdateWrapper通常只有在 set语句比较特殊才使用
- 尽量使用 LambdaQueryWrapper和 LambdaUpdateWrapper避免硬编码
1
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// 原sql:SELECT id,username,info,balance FROM user WHERE username LIKE ? AND balance >= ?
void testQuerywrapper() {
QueryWrapper<User> wrapper = new QueryWrapper<User>() // 1.构建查询条件
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper); // 2.查询
}
// 使用 Lambda替代上方法中的硬编码
void testLambdaQuerywrapper() {
LambdaQuerywrapper<User> wrapper = new LambdaQuerywrapper<User>()
.select(User::getid, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000)
List<User> users = userMapper.selectList(wrapper);
}
// 原sql:UPDATE user SET balance = 2000 WHERE (username = "jack")
void testUpdateByQuerywrapper() {
User user = new User()// 1.要更新的数据
user.setBalance(2000);
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");// 2.更新的条件
userMapper.update(user, wrapper);// 3.执行更新
}
// 原sql:UPDATE user SET balance = balance - 200 WHERE id in (1,2,4)
void testUpdatewrapper() {
List<Long> ids = List.of(1L,2L,4L);
Updatewrapper<User> wrapper = new Updatewrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids) ;
userMapper.update(null, wrapper) ;
}
自定义SQL
我们可以利用 MyBatisPlus的 Wrapper来构建复杂的 Where条件,然后自己定义SQL语句中剩下的部分。
在业务层编写wrapper包含sql中的where部分,在mapper方法声明wrapper变量名称“ew”,最后在mapper对应的xml中自定义sql编写where以外的部分(解决了 不能在业务层编写sql 和 使用mp简化查询语句编写 的矛盾??
IService接口
- 简单业务方法,直接在controller中调用对应的IService中的方法;
对于复杂业务,需要在自定义Servicelmpl中编写逻辑,调用对应的BaseMapper中的方法;
当BaseMapper不足以满足需求时,需要在mapper中编写自定义sql(处理where之外的sql,如update…);
对于mapper中自定义sql,简单的使用注解编写,复杂的在xml中编写。。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 1、自定义Service接口继承IService接口
public interface IUserService extends IService<User> {
}
// 2、自定义Service实现类,实现自定义接口并继承Servicelmpl类(否则要自己一个个实现IService接口的方法)
public class UserServiceImpl
extends ServiceImpl<UserMapper, User> // 泛型中指定mapper和实体类类型
implements IUserService {
}
// 使用方法
class IUserServiceTest {
private IUserService userService;
void testSaveUser() {
User user = new User();
user.setUsername(uLiLeiu);
user.setPassword("123");
userService.save(user); // 如果方法与BaseMapper中的重复,就不需要BaseMapper了??
}
} - IService的 Lambda方法:在 自定义的 Servicelmpl类中进行 复杂操作;
- 需求:复杂查询,查询条件如下(name: 用户名关键字,可以为空;status:用户状态,可以为空;minBalance:最小余额,可以为空;maxBalance:最大余额,可以为空)
1
2
3
4
5
6
7
8public List<User> queryUsers(String name, Integer status, Integer minBalance Integer maxBalance) {
return lambdaQuery()
.like(name != null, User::getUsername, name)
.eg(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null,User::getBalance, maxBalance)
.list(); // 如果查询一个记录就是.one
} - 需求:复杂更新,要求如下(按id更新,更新为扣后余额,如果扣减后余额为0,则将用户status修改为冻结状态(2))
1
2
3
4
5
6
7
8
9
10
11
12
public List<User> deductBalance(Long id,Integer money) {
...
// 4.扣城余额 update tb_user set balance = balance - ?
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0, User::getStatus, 2) // 如果余额0,修改状态
.eq(User::getId, id) // 相当于 where
.eq(User::getBalance,user.getBalance()) // 乐观锁
.update(); // 更新
}
- 需求:复杂查询,查询条件如下(name: 用户名关键字,可以为空;status:用户状态,可以为空;minBalance:最小余额,可以为空;maxBalance:最大余额,可以为空)
- IService批量新增(批处理):开启 rewriteBatchedStatements=true参数
分页插件
首先,要在配置类中注册MyBatisplus的核心插件,同时添加分页插件
接着,就可以使用分页的API了
1 |
|
1 | void testPageQuery() { |