图片来源官网
首先在使用之前,我们先使用Mybatis完成CRUD操作,之后对Mybatis与Mybatis-Plus进行整合操作。
一、创建项目之后,首先创建数据库中的数据对象
-- 创建测试表
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_name` varchar(20) NOT NULL COMMENT '用户名',
`password` varchar(20) NOT NULL COMMENT '密码',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- 插入测试数据
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('1', 'zhangsan', '123456', '张三', '18', '[email protected]');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('2', 'lisi', '123456', '李四', '20', '[email protected]');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('3', 'wangwu', '123456', '王五', '28', '[email protected]');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('4', 'zhaoliu', '123456', '赵六', '21', '[email protected]');
INSERT INTO `tb_user` (`id`, `user_name`, `password`, `name`, `age`, `email`) VALUES
('5', 'sunqi', '123456', '孙七', '24', '[email protected]');
二、创建com.company.pojo.User类,作为查询结果的POJO映射类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
上面的两个注解:@NoArgsConstructor
和 @AllArgsConstructor
分别对应直接生成无参构造和全参构造。
三、创建com.company.mapper.UserMapper接口,对应查询操作的接口
public interface UserMapper {
List<User> findAll();
}
四、创建资源文件log4j.properties文件,使用log4j生成日志
log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
五、创建Mybatis核心配置文件mybatis-config.xml
<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/mp?
useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQuerie
s=true&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
六、创建UserMapper.xml配置文件,对应sql操作的语句
<mapper namespace="com.company.submodule.mapper.UserMapper">
<select id="findAll" resultType="com.company.submodule.pojo.User">
select * from tb_user
select>
mapper>
七、使用测试类对上述的结果进行测试
public class MybatisTest {
@Test
public void testFindAll() throws IOException {
// 1. 创建 sqlSessionFactory
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 2. 创建mapper对象
SqlSession sqlSession = sessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 3. 运行测试
List<User> users = mapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
最终打印出查询结果。
上述的使用是针对Mybatis框架的使用,针对Mybatis-Plus框架的使用,我们首先要在Maven中添加依赖:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plusartifactId>
<version>3.1.1version>
dependency>
现在针对Mybatis和Mybatis-Plus框架的整合操作,整个整合过程中仅需要修改两处地方:
①将之前创建的UserMapper接口继承BaseMapper
public interface UserMapper extends BaseMapper<User> { List<User> findAll();}
该接口提供了一系列的方法,Mybatis-Plus提供的一系列方法就在这个里面提供。该接口的归属包为 package com.baomidou.mybatisplus.core.mapper;
。
②测试代码中:
创建sqlSession的时候,使用Mybatis-Plus提供的类,而不是使用Mybatis提供的类。
public class MybatisPlusTest {
@Test
public void testFindAll() throws IOException {
// 1. 创建 sqlSessionFactory
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
// 2. 创建mapper对象
SqlSession sqlSession = sessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 3. 运行测试
List<User> users = mapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
查询结果和之前的结果保持一致。
测试中使用Mybatis-Plus中的方法,selectList()
调用方法:
List<User> users = mapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
这样直接使用的时候会报错,提示 Table 'mp.user' doesn't exist
。
提示该错误的原因是因为使用的POJO类,命名为User,Mybatis-Plus在运行的时候会指定类名为查询的数据库中的表格名(查询方法运行的时候参数为null
)。其中的mp为之前创建的实例的数据库名,查看运行日志可以得知,当时生成的Sql语句为 SELECT id,user_name,password,name,age,email FROM user
,表名不一致,所以要在User类上指定@TableName
的值。
解决办法:在之前创建的POJO包下的User类的上方添加注释
@TableName("tb_user")
这样的作用是指定表名,将数据库中的表格名和Mybatis-Plus中指定的表名匹配一致。
疑问:
创建Mybatis-Plus的工厂类对象之后,创建Mapper对象的时候已经指定了UserMapper类,那么为什么运行时候结果没有指定到UserMapper.xml文件中的语句块,而是需要重新再指定TableName呢?
为什么继承之后就能够直接使用Mybatis-Plus之中提供的方法了呢?
一、首先导入必要的依赖pom.xml中
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.0.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.0.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.5.RELEASEversion>
dependency>
二、创建实体类pojo.User类
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
三、创建mapper.UserMapper接口
public interface UserMapper extends BaseMapper<User> {
}
四、创建Spring的核心配置文件applicationContext.xml文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="driver" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="5"/>
bean>
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.company.mybatis.mapper"/>
bean>
beans>
五、创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MybatisPlusSpringTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList() {
List<User> users = this.userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
这个时候直接运行测试类可能会报错,因为两者不在同一个包下。所以可能无法创建dataSource
的Bean对象,原因是无法加载资源路径下的jdbc.properties文件。
解决方法一:将resources文件夹复制粘贴到test下面
解决方法二:不使用jdbc.properties文件,直接使用字符串类型的value值
根据上述的写法,还会发生错误:
Cannot convert value of type ‘java.lang.String’ to required type 'java.sql.Driver"
解决方法:
因为使用的是Druid数据源,Druid数据源对于配置的name都有具体的要求。需要将driver改成driverClassName
才能解决错误。
一、用自动生成的方式创建Spring Boot项目
二、编写UserMapper接口
@Repository
public interface UserMapper extends BaseMapper<User> {
}
添加该注解的是为了避免后面用@Autowired
会造成警告。
三、编写POJO类User
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String email;
}
这样编写需要在初始化Spring Boot项目的时候导入lombok依赖,目的是为了能够使用注解简化代码。
四、在Spring Boot的启动类上添加 @MapperScan
注解,导入UserMapper
接口
@SpringBootApplication
// 设置mapper接口的扫描包
@MapperScan("com.example.mybatisplus_springboot.mapper")
public class MybatisPlusSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusSpringBootApplication.class, args);
}
}
五、编写测试类,利用selectList(null)
方法查询User结果最终遍历
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
class MybatisPlusSpringBootApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void selectList() {
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
出现的问题:
Invalid bound statement (not found): com.example.mybatisplus_springboot.mapper.UserMapper.selectList
根据排查各种情况,最终发现是导入的依赖包的问题。原先导入的依赖包是普通的依赖包,不是Spring Boot版本的Mybatis-Plus依赖,不适用Mapper映射文件而直接使用接口导入的方式,会发生映射Mapper和方法无法形成映射关系。
解决方法是修改依赖包为:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.1.1version>
dependency>
运行的结果如下所示:
Mybatis-Plus框架使用的时候相比于Mybatis更加简单。根据上述的Spring Boot项目,我们在使用Mybatis-Plus的时候,只需要完成:
①添加POJO类对象
②添加Mapper接口,并让之继承BaseMapper接口
③在SpringBoot启动类上添加@MapperScan
扫描Mapper接口
④创建Mapper接口对象,然后注入ioc容器
⑤使用Mapper创建的对象,调用BaseMapper中提供的各种简化CRUD的方法
根据源码,键入Alt+7可以查看到BaseMapper中提供的一些方法:
由于数据库中已经设置ID自增长,我们可以不需要设置ID值,填充其他属性来完成。
编写测试类InsertUserTest类,如下所示:
@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic class InsertUserTest { @Autowired private UserMapper userMapper; @Test public void testInsert() { User user = new User(); user.setEmail("[email protected]"); user.setUserName("Real"); user.setName("雨下一整晚"); user.setAge(20); user.setPassword("123456"); // 返回的是数据库影响的行数 int insert = this.userMapper.insert(user); System.out.println("Result >= " + insert); // 获取自增长后的id,插入成功后自增站的id值会回填到user对象中 System.out.println("id >= " + user.getId()); }}
上述代码中,调用userMapper的insert方法,传入设置好参数的user对象(ID不设置)。
这个时候能够实现插入成功。但是主键却变成了32位的数字,不符合我们的使用要求。
@TableId
解决方法:
一、在POJO类对象的定义中,对于主键的属性添加注解
@TableId(type = IdType.AUTO)private Long id;
二、添加注解后,还是出现32位主键的情况。通过查看他人的解决方法,先将数据库中的主键自增长取消勾选,之后又重新勾选,解决
运行结果最终可以看到结果:
@TableField
@TableField
注解主要作用:
一、解决数据库中字段名和pojo类对象的属性名不一致的问题
例如:将email属性更改为mail属性,需要在mail的成员变量的地方添加该注解
@TableField(value = "email") // 解决数据库中字段名不一致的问题
private String mail;
同时更改set方法的方法名。
二、解决数据库中字段名不存在的问题
如果新增加的字段在数据库中没有相应的字段属性,那么应该屏蔽该数据字段的插入或者删除操作。
@TableField(exist = false) // 解决数据库中字段不存在的问题
private String address;
之后设置该属性后实现插入操作,会发现插入语句中没有该属性值,也就是插入语句自动屏蔽该成员变量。
三、屏蔽数据库中某些字段的查询结果
数据库中某些关键的属性是不应该被查询到的,比如说密码,我们可以设置:
@TableField(select = false) // 屏蔽字段查询结果
private String password;
该注解的作用很明显,屏蔽select语句的字段结果。
查询出来之后,显示password的值为null
。
update方法在Mybatis-Plus中存在两个,分别是根据ID进行更新和根据条件进行更新。
int updateById(@Param("et") T entity);int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
updateById()
测试:
根据代码测试可以看到,该方法需要传入的是一个user实例对象,首先需要创建一个对象。
然后对该对象的主键ID进行设置,设置成数据库中需要修改的字段的ID值,然后设置需要更改的属性,最终传入对象。
@Testpublic void testUpdateById() { User user = new User(); user.setId(6L); user.setName("钱八"); user.setUserName("qianba"); user.setMail("[email protected]"); userMapper.up int update = userMapper.updateById(user); // 返回的是数据库影响的行数 System.out.println(update); User selectUser = userMapper.selectById(6L); System.out.println(selectUser);}
运行结果:
1
User(id=6, userName=qianba, password=null, name=钱八, age=20, [email protected])
根据条件修改:
根据测试,该方法需要传入的是一个user对象以及包装器wrapper
对象。
我们先编写测试方法,设置个别需要修改的属性。
User user = new User();user.setAge(20);user.setPassword("888888");
这个时候我们需要查询到我们需要修改的row数据,所以上述的实现类使用QueryWrapper类。代码如下:
@Testpublic void testUpdate() { User user = new User(); user.setAge(20); user.setPassword("888888"); // Wrapper 包装器对象的编写 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_name", "zhangsan"); int update = userMapper.update(user, queryWrapper); System.out.println(update); System.out.println(userMapper.selectById(1L));}
运行结果:
此外,根据条件更新还有另外一种写法:(直接使用上述包装器的是实现对象之一的UpdateWrapper类)
@Test
public void testDirectUpdate() {
// Wrapper 包装器对象
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set("password", "999999").eq("user_name", "zhangsan");
int update = userMapper.update(null, wrapper);
System.out.println(update);
System.out.println(userMapper.selectById(1L));
}
删除的方法常见的有下面几种:
deleteById()
方法测试:
使用方法很简单,直接传入ID值即可。
@Test
public void testDeleteById() {
int delete = userMapper.deleteById(6L);
System.out.println(delete);
System.out.println(userMapper.selectList(null));
}
运行结果就是删除主键为6的元素。
deleteByMap()
方法测试:
该方法需要传入一个Map类型的参数,首先需要创建一个Map类型的对象。
@Testpublic void testDeleteByMap() { Map<String, Object> map = new HashMap<>(); map.put("user_name", "zhangsan"); map.put("password", "123456"); // password is “999999” // the relationship of maps is AND in sql statement System.out.println(userMapper.deleteByMap(map)); // 0 System.out.println(userMapper.selectById(1L)); // same as before}
map的参数前者和后者的关系是colomu和value的关系,使用的时候要注意二者的关联。而且多个mao属性之间的关系在sql语句映射中是AND的关系,所以根据条件,这一语句密码不符合,删除失败。
delete()
方法测试:
使用delete()
进行测试,直接使用delete方法,传入一个Wrapper类型的参数。
这种方式有两种构建形式:实际使用中推荐第二种方式,可以避免字段名输错的情况
@Testpublic void testDelete() { // 方法一:创建 Wrapper对象 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("id", "5"); System.out.println(userMapper.delete(wrapper)); // 1 System.out.println(userMapper.selectById(5L)); // null // 方法二:创建user对象 User user = new User(); user.setId(5L); user.setPassword("123456"); QueryWrapper queryWrapper = new QueryWrapper<>(user); System.out.println(userMapper.delete(queryWrapper)); // 0 System.out.println(userMapper.selectById(5L)); // null}
deleteBatchIds()
方法测试:
该方法是根据传入的Id值进行批量删除。
@Testpublic void testDeleteBatchIds() { // 根据id进行批量删除 int delete = userMapper.deleteBatchIds(Arrays.asList(7L, 8L)); // 2 System.out.println(delete);}
selectById()
方法测试:
根据主键Id查询字段值:
@Testpublic void testSelectById() { User user = userMapper.selectById(2); System.out.println(user);}
selectBatchIds()
方法测试:
根据主键Id进行批量查询的操作,实施步骤一样:
@Testpublic void testSelectBatchIds() { // 根据id批量查询 List users = userMapper.selectBatchIds(Arrays.asList(1L, 2L)); for (User user : users) { System.out.println(user); }}
根据多个id值,返回多个查询结果,如果没有,则为null
selectOne()
方法测试:
根据条件查询,返回一条数据。
@Testpublic void testSelectOne() { // 根据条件查询单个 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("user_name", "wangwu"); // 查询到的数据超过一条时,会抛出异常 User user = userMapper.selectOne(wrapper); System.out.println(user);}
如果查询的结果有多条,将会抛出异常。
selectCount()
方法测试:
根据条件查询,返回查询到的数据的条数。
@Testpublic void testSelectCount() { // 根据条件查询,返回数据条数 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.gt("age", "20"); // age > 20 , count = 2 Integer count = userMapper.selectCount(wrapper); System.out.println(count); // 2}
selectList()
方法测试:
根据条件,查询全部记录,返回entity对象。
@Testpublic void testSelectList() { // 根据条件查询全部记录 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.like("email", "@"); // like %@% List users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
selectPage()
方法测试:
该方法是分页查询,对查询到的结果进行分页,方便后续处理到数据。
一、配置Mybatis-Plus的分页插件
@Configuration// 设置mapper接口的扫描包@MapperScan("com.example.mybatisplus_springboot.mapper")public class MybatisPlusConfig { @Bean // 配置mybatisPlus的分页插件 public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }}
该操作将之前配置的扫描Mapper包的注解从Spring Boot的启动类拿到Mybatis-Plus的配置类。顺便将方法中新创建的分页类注入到ioc容器中。
二、使用selectPage(Page page, Wrapper wrapper)
方法
该方法需要传入两个参数,一个是设置分页的参数,一个是设置查询的条件。
@Testpublic void testSelectPage() { // 根据条件进行分页查询 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.like("email", "@"); Page page = new Page<>(1, 1); IPage iPage = userMapper.selectPage(page, wrapper); System.out.println("总条数:" + iPage.getTotal()); System.out.println("总页数:" + iPage.getPages()); System.out.println("当前页:" + iPage.getCurrent()); List records = iPage.getRecords(); for (User record : records) { System.out.println(record); }}
在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类,实现关系如下:
主要是使用AbstractSqlInjector
中的inspectInject()
方法对SQL进行注入,关键语句是使用Lambda表达式的语句。
在Spring Boot项目中的application.properties文件中指定mybatis的配置文件所在的路径。
# 全局配置文件mybatis-plus.config-location=classpath:mybatis-config.xml
这样配置之后可以扫描到配置文件。
如果系统中执行的SQL语句在MP插件中查询不到,需要指定的SQL语句,这个时候就需要自定义的SQL语句。
一、首先配置扫描Mapper映射配置文件所在路径
# 指定 mapper.xml 文件的路径mybatis-plus.mapper-locations=classpath*:mybatis/*.xml
二、在刚才指定的路径下编写相关的映射配置文件 UserMapper.xml
<mapper namespace="com.example.mybatisplus_springboot.mapper.UserMapper"> <select id="findById" resultType="com.example.mybatisplus_springboot.pojo.User"> select * from tb_user where id = #{id} select>mapper>
三、在UserMapper接口中添加自定义的方法
public interface UserMapper extends BaseMapper<User> { User findById(Long id);}
四、编写运行自定义的SQL语句的方法
@Test
public void testFindById() {
User user = userMapper.findById(1L);
System.out.println(user);
}
该配置的目的是设置UserMapper中的全路径名,使其使用的时候更加方便。
# 实体包扫描路径
mybatis-plus.type-aliases-package=com.example.mybatisplus_springboot.pojo
添加之后,可以直接使用该包下的类,不需要指定该路径。
<select id="findById" resultType="User">
select * from tb_user where id = #{id}
select>
如上所示,User类属于上述实体包的路径,不需要设置全路径,而是直接写类名即可。
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射,默认值为true
,表示开启。
之前的应用中,使用的是user_name的数据库命名,在Java中写的是userName的命名,二者没有设置却能够正常查询出来并实现映射,证实了该注解是默认开启的。
# 关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
运行一个查询,可以看到查询结果为null
User(id=1, userName=null, password=999999, name=张三, age=20, mail=null)
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
# 禁用缓存mybatis.configuration.cache-enabled=false
配置ID的生成策略,默认值为ID_WORKER
# idType 策略配置,自增长mybatis-plus.global-config.db-config.id-type=auto
配置完毕之后,就不需要在实体pojo映射类的id属性上添加@TableId(type = IdType.AUTO)
的注解了。
表名前缀,全局配置后可省略@TableName()
配置。
# 配置表名前缀mybatis-plus.global-config.db-config.table-prefix=tb_
配置数据库中表名的前缀,省去了后续编写pojo类的时候在类的上方指定@TableName("tb_user")
的注解了。
在MP中,Wrapper包装器的实现如下所示:
一共有两个抽象类,并拥有各自的具体实现类。
该方法存在于QueryWrapper
类中,可以指定查询的条件。
@Testpublic void testAllEq() { QueryWrapper<User> wrapper = new QueryWrapper<>(); // 设置allEq的参数条件 Map map = new HashMap<>(); map.put("name", "张三"); map.put("age", "20"); map.put("password", null); // 单个参数,多个map值之间的关系是AND,查询结果为空 // wrapper.allEq(map); // 两个参数,设置为null的参数是否指定为查询条件,查询有结果 // wrapper.allEq(map, false); // Lambda表达式参数,表示满足条件则添加进where条件中,有多个条件满足则使用AND连接 wrapper.allEq((K, V) -> ("name".equals(K) || "age".equals(K) || "id".equals(K)), map); List users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
主要使用的方法如上所示。该方法是重载的形式创建的,存在多种形式。
Mybatis-Plus中存在多种比较操作,内置的有:
@Testpublic void testLike() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.likeLeft("user_name", "wu"); List<User> users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
@Testpublic void testOrderByAgeDesc() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("age"); List<User> users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
@Testpublic void testOr() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_name", "wangwu").or().eq("age", 20); List<User> users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
@Testpublic void testSelect() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_name", "wangwu").or() .eq("age", 20) .select("user_name", "name", "age"); // 指定查询的字段 List users = userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记 录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而 且简洁易懂。
ActiveRecord的主要思想是:
- 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;
- 通常表的每个字段 在类中都有相应的Field; ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;
- ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;
在Mybatis-Plus中,只需要将实体类pojo对象继承Model即可。
@Data@AllArgsConstructor@NoArgsConstructor//@TableName("tb_user")public class User extends Model { //@TableId(type = IdType.AUTO) private Long id; private String userName; @TableField(select = false) // 屏蔽字段查询结果 private String password; private String name; private Integer age; //@TableField(value = "email") // 解决数据库中字段名不一致的问题 private String email; /* @TableField(exist = false) // 解决数据库中字段不存在的问题 private String address;*/}
@Testpublic void testInsert() { User user = new User(); user.setUserName("qianqi"); user.setPassword("123456"); user.setName("钱七"); user.setAge(25); user.setEmail("[email protected]"); boolean insert = user.insert(); System.out.println(insert); User user1 = user.selectById(); System.out.println(user1);}
@Test
public void testUpdate() {
User user = new User();
user.setId(3L);
user.setAge(26);
boolean update = user.updateById();
System.out.println(update);
}
此外,还可以根据条件进行更新操作,使用Wrapper包装器完成。
@Test
public void testDelete() {
User user = new User();
user.setId(4L);
boolean delete = user.deleteById();
System.out.println(delete);
}
@Test
public void testSelect() {
User user = new User();
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 22); // 查询年龄>=22岁的用户
List<User> users = user.selectList(wrapper);
for (User user1 : users) {
System.out.println(user1);
}
}
如果使用的是Oracle数据库,那么就不能使用自增长,就得使用Sequence序列生成id值了。
创建表格以及序列
--创建表,表名以及字段名都要大写CREATE TABLE "TB_USER" ("ID" NUMBER(20) VISIBLE NOT NULL ,"USER_NAME" VARCHAR2(255 BYTE) VISIBLE ,"PASSWORD" VARCHAR2(255 BYTE) VISIBLE ,"NAME" VARCHAR2(255 BYTE) VISIBLE ,"AGE" NUMBER(10) VISIBLE ,"EMAIL" VARCHAR2(255 BYTE) VISIBLE)--创建序列CREATE SEQUENCE SEQ_USER START WITH 1 INCREMENT BY 1
jdbc驱动包
通过Maven仓库不能直接下载到Oracle的驱动包,所以我们需要自己下载之后导入,并且添加as Library
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc8 -Dversion=12.1.0.1 -Dpackaging=jar -Dfile=ojdbc8.jar
安装完成的坐标:
<dependency> <groupId>com.oraclegroupId> <artifactId>ojdbc8artifactId> <version>12.1.0.1version>dependency>
修改数据库连接配置
#数据库连接配置spring.datasource.driver-class-name=oracle.jdbc.OracleDriverspring.datasource.url=jdbc:oracle:thin:@192.168.31.81:1521:xespring.datasource.username=systemspring.datasource.password=oracle#id生成策略mybatis-plus.global-config.db-config.id-type=input
使用前:需要完成两个步骤:
一、需要配置MP的序列生成器到Spring容器:
@Configuration@MapperScan("cn.itcast.mp.mapper") //设置mapper接口的扫描包public class MybatisPlusConfig { /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } /** * 序列生成器 */ @Bean public OracleKeyGenerator oracleKeyGenerator(){ return new OracleKeyGenerator(); }}
二、实体pojo类对象中指定序列的名称
@KeySequence(value = "SEQ_USER", clazz = Long.class)public class User{ // ......}
使用:
@RunWith(SpringRunner.class)@SpringBootTestpublic class UserMapperTest { @Autowired private UserMapper userMapper; @Test public void testInsert(){ User user = new User(); user.setAge(20); user.setEmail("[email protected]"); user.setName("曹操"); user.setUserName("caocao"); user.setPassword("123456"); int result = this.userMapper.insert(user); //返回的result是受影响的行数,并不是自增后的id System.out.println("result = " + result); System.out.println(user.getId()); //自增后的id会回填到对象中 }}
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法
调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的
一些方法等。总体概括为:
- 拦截执行器的方法
- 拦截参数的处理
- 拦截结果集的处理
- 拦截Sql语法构建的处理
MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不适用于生产环境。
首先在MybatisPlusConfig类中配置插件:
@Bean // SQL分析插件配置public SqlExplainInterceptor sqlExplainInterceptor() { SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor(); List list = new ArrayList<>(); list.add(new BlockAttackSqlParser()); // 攻击阻断器,阻断删除、更新操作 sqlExplainInterceptor.setSqlParserList(list); return sqlExplainInterceptor;}
之后编写测试方法:
/** * 全表更新测试,测试阻断器是否生效 */@Testpublic void testUpdateAll() { User user = new User(); user.setAge(20); boolean update = user.update(null); System.out.println(update);}
运行结果如下:
org.apache.ibatis.exceptions.PersistenceException:
Error updating database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
可以看到全表更新操作成功被阻断,插件生效。
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
该插件只用于开发环境,不建议生产环境使用。
配置方法:
在Mybatis-config.xml文件中添加配置选项:
<configuration> <plugins> <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor"> <property name="maxTime" value="100" /> <property name="format" value="true" /> plugin> plugins>configuration>
配置完成之后在application.properties文件中导入该文件,并解决冲突,即可生效。
选择一个Test方法运行之后,可以看到运行结果。
Time:27 ms - ID:com.example.mybatisplus_springboot.mapper.UserMapper.selectById
Execute SQL:
SELECT
id,
user_name,
name,
age,
FROM
tb_user
WHERE
id=2
当需要修改一条数据的时候,希望这条数据没有被别人修改。
乐观锁实现原理:
简而言之,就是添加一个新的字段,每次修改操作同时带着该字段修改。修改操作之前对比字段是否相对于自己获取的字段值发生变化。
使用准备:
一、在项目中配置插件(两种方式):
在mybatis-config.xml文件中配置插件:
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor">plugin>
在mybatisPlusConfig类中添加配置:
@Bean // 乐观锁插件
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
二、在数据库中新增字段Version:
ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';
三、为实体pojo对象添加Version字段,并添加Version注解
@Versionprivate String version;
使用步骤:
编写测试方法,使用ActiveRecord方式调用修改。
@Testpublic void testUpdateVersion() { User user = new User(); user.setId(1L); user.setAge(26); user.setVersion(1); boolean update = user.updateById(); System.out.println(update);}
执行的SQL语句如下所示:
UPDATE tb_user
SET age=26, version=2
WHERE id=1 AND version=1
最终id为1的用户年龄成功修改成26,version版本的值自动+1,达到版本更新的效果。
需要注意的是,乐观锁在运用的时候,版本的字段需要指定为version,不能随意更改。
注意事项:
int,Integer,long,Long,Date,Timestamp,LocalDateTime
newVersion = oldVersion + 1
newVersion
会回写到 entity
中updateById(id)
与 update(entity, wrapper)
方法update(entity, wrapper)
方法下, wrapper
不能复用在MP中,如果需要自定义sql方法,可以通过在接口中声明该方法,之后在UserMapper.xml之类的映射文件中添加语句配置实现。
但是,相比于这种传统的方法,我们有更好的方式来将sql语句方法传入ioc容器,实现SQL注入。优点是可以实现在多表之间的方法复用。
一、编写MyBaseMapper接口
public interface MyBaseMapper<T> extends BaseMapper<T> { List<T> findAll();}
二、将自定义编写的接口继承该接口
@Repositorypublic interface UserMapper extends MyBaseMapper<User> { User findById(Long id);}
三、编写MySqlInjector
类
public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList() { List<AbstractMethod> methodList = super.getMethodList(); methodList.add(new FindAll()); // 再扩充自定义的方法 // list.add(new FindAll()); return methodList; }}
四、编写FindAll类
public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { String sqlMethod = "findAll"; String sql = "select * from " + tableInfo.getTableName(); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addSelectMappedStatement(mapperClass, sqlMethod, sqlSource, modelClass, tableInfo); }}
五、将自定义的SQL注入器注入到ioc容器
@Bean // 自定义SQL注入器public MySqlInjector mySqlInjector(){ return new MySqlInjector();}
六、编写测试代码
/** * 自定义 SQL 注入器方法测试 */@Testpublic void testFindAll(){ List<User> users = userMapper.findAll(); for (User user : users) { System.out.println(user); }}
最终运行可以查询出该表格所有数据。
在MP中,可以根据自己的需要实现自动填充某些字段的值。
一、编写自动填充策略类
@Componentpublic class MyMetaObjectHandle implements MetaObjectHandler { /** * 插入数据的时候填充 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { Object password = getFieldValByName("password", metaObject); if (null == password) { //字段为空,可以进行填充 setFieldValByName("password", "123456", metaObject); } } /** * 更新数据的时候填充 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { }}
二、在需要进行自动填充的pojo类的相应字段上添加注解
// 屏蔽字段查询结果,插入数据时填充字段@TableField(select = false, fill = FieldFill.INSERT)private String password;
三、编写测试类
/** * 测试自动填充字段 */@Testpublic void testInsert() { User user = new User(); user.setUserName("qianqi"); // user.setPassword("123456"); user.setName("钱七"); user.setAge(25); user.setEmail("[email protected]"); boolean insert = user.insert(); System.out.println(insert); User user1 = user.selectById(); System.out.println(user1);}
最终没有设置密码的情况下还是能够实现自动填充默认密码字段。填充的条件是密码值为null,没有设置密码值,就会使用自动填充字段的策略。
在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。
整体的实现逻辑类似于乐观锁,需要在数据表中新增一个字段,用来标记是否删除的状态。
一、添加字段
ALTER TABLE `tb_user`ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER`version`;
二、在pojo实体类中添加该字段,并添加相应注解
@TableLogicprivate Integer deleted;
三、配置逻辑删除
在application.properties文件中添加删除的逻辑映射
# 逻辑已删除值(默认为 1)mybatis-plus.global-config.db-config.logic-delete-value=1# 逻辑未删除值(默认为 0)mybatis-plus.global-config.db-config.logic-not-delete-value=0
四、编写测试
/** * 逻辑删除测试 */@Testpublic void testDeleteById(){ this.userMapper.deleteById(4L);}
执行的SQL语句如下:
UPDATE tb_user SET deleted=1 WHERE id=4 AND deleted=0
查询被删除的数据结果:
/** * 逻辑删除测试查询结果 */@Testpublic void testSelectById(){ User user = this.userMapper.selectById(4L); System.out.println(user);}
执行的SQL语句为:
SELECT id, user_name, name, age, email, version, deleted FROM tb_user WHERE id=4 AND deleted=0
结果显示为null
,但是在数据库中查询可得知,只是将deleted
字段的值设置为1,并没有将数据从磁盘上删除。
一、修改表格结构
ALTER TABLE `tb_user`ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;
二、配置枚举类
public enum SexEnum implements IEnum<Integer> { MAN(1, "男"), WOMAN(2, "女"); private int value; private String desc; SexEnum(int value, String desc) { this.value = value; this.desc = desc; } @Override public Integer getValue() { return this.value; } @Override public String toString() { return this.desc; }}
三、配置添加枚举类的扫描
在application.properties中添加配置扫描枚举
# 枚举包扫描
mybatis-plus.type-enums-package=com.example.mybatisplus_springboot.enums
四、在pojo实体类中添加枚举字段
private SexEnum sex; // 性别枚举字段
五、编写测试类
/**
* 通用枚举测试
*/
@Test
public void testInsertEnum(){
User user = new User();
user.setName("钱七");
user.setUserName("qianqi");
user.setAge(20);
user.setEmail("[email protected]");
user.setVersion(1);
user.setSex(SexEnum.WOMAN);
int result = this.userMapper.insert(user);
System.out.println("result = " + result);
}
执行的SQL语句:
INSERT INTO tb_user( user_name, password, name, age, email, version, sex )
VALUES ( 'qianqi', '123456', '钱七', 20, '[email protected]', 1, 2 )
通用枚举查询测试:
/** * 测试枚举查询 */@Testpublic void testSelectBySex() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("sex", SexEnum.WOMAN); List<User> users = this.userMapper.selectList(wrapper); for (User user : users) { System.out.println(user); }}
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
package com.company.mp.generator;
import java.util.Scanner;*
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 代码生成器演示例子
*/
public class MysqlGenerator {
/**
* 读取控制台内容
* @param tip
* @return
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
/**
* run this
* @param args
*/
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("itcast");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://127.0.0.1:3306/mp?useUnicode = true & useSSL = false & characterEncoding = utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("cn.itcast.mp.generator");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return projectPath + "/itcast-mpgenerator/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" +
StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//
strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseE ntity");
strategy.setEntityLombokModel(true);
//
strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.B aseController");
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。 安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
Java 与 XML 调回跳转
Mapper 方法自动生成 XML
Java与XML来回跳转:
点击Mybatis的图片会自动实现跳转:
点击上图的mybatis的小鸟logo,会跳转到下面的地方:
Mapper方法自动生成XML:
在UserMapper接口中添加方法,然后选中高亮选项:
会在UserMapper.xml文件中自动生成下面的XML代码(SQL语句需要自己写)