「这是我参与2022首次更文挑战的第36天,活动详情查看:2022首次更文挑战」
二、通用Mapper实现基本增删改查-BaseMapper
2.5 insert 相关方法
// 保存一个实体,null的属性也会保存,不会使用数据库默认值
int insert(T record);
// 保存一个实体,null的属性不会保存,会使用数据库默认值
int insertSelective(T record);
复制代码
2.5.1 insert 方法
在PorscheService中增加save方法
void save(Porsche porsche);
复制代码
在PorscheServiceImpl中实现save方法的实现
@Override
public void save(Porsche porsche) {
porscheMapper.insert(porsche);
}
复制代码
在PorscheServiceTest中对save方法进行测试
@Test
public void save(){
// 构造插入的实体类
Porsche porsche = new Porsche();
porsche.setPorName("Panamera 4S 2022");
porsche.setPorPrice(880000d);
porsche.setPorStock(10);
porscheService.save(porsche);
}
复制代码
执行save测试方法 数据库更新了一行,插入成功。增加测试代码,输出自增的主键,再次执行测试
输出主键为null,实际插入数据库后已经生成了自增主键,只是程序没有获取到插入成功后生成的主键。 要获取生成的主键Value需要在Porsche实体类与数据主主键对应的属性上增加@GeneratedValue(strategy = GenerationType.IDENTITY) 再次执行测试
执行了两条SQL语句,一条插入语句,一条是查询最新的主键的Value,控制台成功输出插入成功后生成的自增主键的Value。
@GeneratedValue注解可以让通用Mapper在执行insert操作之后将数据库自动生成的自增主键的值回写到实体类中
2.5.2 insertSelective 方法
在PorscheService中增加savePorscheSelective方法
void savePorscheSelective(Porsche porsche);
复制代码
在PorscheServiceImpl中增加savePorscheSelective方法的实现
@Override
public void savePorscheSelective(Porsche porsche) {
porscheMapper.insertSelective(porsche);
}
复制代码
增加savePorscheSelective测试方法
@Test
public void savePorscheSelective(){
Porsche porsche = new Porsche();
porsche.setPorName("Porsche 911 2022");
porscheService.savePorscheSelective(porsche);
}
复制代码
非主键字段如果为null,就不会被加入到SQL语句中
适合实体类属性较多,但是执行插入时插入属性部位NULL的数据比较少的情况
2.6 update 相关方法
// 根据主键更新实体全部字段,null值会被更新
int updateByPrimaryKey(T record);
// 根据主键更新属性不为null的值
int updateByPrimaryKeySelective(T record);
复制代码
2.6.1 updateByPrimaryKeySelective
在PorscheService中增加updatePorscheSelective方法
void updatePorscheSelective(Porsche porsche);
复制代码
在PorscheServiceImpl中增加updatePorscheSelective方法的实现
@Override
public void updatePorscheSelective(Porsche porsche) {
porscheMapper.updateByPrimaryKeySelective(porsche);
}
复制代码
增加updatePorscheSelective测试方法
@Test
public void updatePorscheSelective(){
Porsche porsche = new Porsche();
porsche.setPorId(1);
porsche.setPorPrice(900000d);
porscheService.updatePorscheSelective(porsche);
}
复制代码
执行测试
针对部分字段更新的实体类使用该方法 针对有值的字段进行更新,没有值的字段保持不变
2.7 delete 相关方法
// 根据实体属性作为条件进行删除,查询条件使用等号
int delete(T record);
// 根据主键字段进行删除,方法参数必须包含完整的主键属性
int deleteByPrimaryKey(Object key);
复制代码
2.7.1 delete
在PorscheService中增加removePorsche方法
void removePorsche(Porsche porsche);
复制代码
在PorscheServiceImpl中增加removePorsche方法的实现
@Override
public void removePorsche(Porsche porsche) {
porscheMapper.delete(porsche);
}
复制代码
增加removePorsche的测试方法
@Test
public void removePorsche(){
// 构造要删除的实体类
Porsche porsche = new Porsche();
porsche.setPorName("Panamera 4S 2022");
porsche.setPorPrice(880000d);
porsche.setPorStock(10);
porscheService.removePorsche(porsche);
}
复制代码
执行测试
如果传入的对象为null,注释掉对象的setter方法后再次执行测试
数据被全部删除,使用这个方法一定要慎重,要进行条件判断
2.7.2 deleteByPrimaryKey
在PorscheService中增加removePorscheById方法
void removePorscheById(Integer id);
复制代码
在PorscheServiceImpl中增加removePorscheById方法的实现
@Override
public void removePorscheById(Integer id) {
porscheMapper.deleteByPrimaryKey(id);
}
复制代码
增加removePorscheById测试方法
@Test
public void removePorscheById(){
// 删除记录的主键
Integer id = 1;
porscheService.removePorscheById(id);
}
复制代码
执行测试
deleteByPrimaryKey最常用的删除的方法
三、通用Mapper实现复杂查询-ExampleMapper
3.1 QBC查询
QBC既Query By Criteria,QBC查询是将查询条件通过Java对象进行模块化封装。
Criterion是一个内部类,是Criterion的复数形式,既准则标准的意思;与MBG生成的复杂查询条件的实体类CatExample中的Criterion内部类代码是一样的,也都提供了createCriteria方法,创建复合查询条件
MBG生成的封装查询条件的XXXExample VS 通用Mapper的Example类 都封装了大量的查询条件。两者使用方法的不同,MBG直接针对Entity实体类生成了XXXExample,如在生成的CatExample、TeacherExample;而通用Mapper使用条件查询时则需要在新建一个Example时传入一个要查询的实体类class
Example porscheExample = new Example(Porsche.class);
复制代码
3.1.1 使用QBC查询
使用QBC创建如下复合查询
WHERE (por_id > 1 and por_stock > 20) OR ( por_price < 1000000 and por_stock > 20)
复制代码
在PorscheService中增加getPorschesByPriceAndStock方法
List<Porsche> getPorschesByPriceAndStock(Example porscheExample);
复制代码
在PorscheServiceImpl中增加getPorschesByPriceAndStock方法的实现
@Override
public List<Porsche> getPorschesByPriceAndStock(Example porscheExample) {
return porscheMapper.selectByExample(porscheExample);
}
复制代码
在PorscheServiceTest增加getPorscheByPriceAndStock的测试方法,查询条件的构建过程查看注释。
@Test
public void getPorscheByPriceAndStock(){
Example porscheExample = new Example(Porsche.class);
// WHERE (por_id > 1 and por_stock > 20) OR ( por_price < 1000000 and por_stock > 20)
// 创建两个查询条件,WHERE关键字后面的两个括号分别对应两个查询条件
Example.Criteria criteria1 = porscheExample.createCriteria();
Example.Criteria criteria2 = porscheExample.createCriteria();
// 设置两个查询条件
criteria1.andGreaterThan("porId",1)
.andGreaterThan("porStock",20);
criteria2.andLessThan("porPrice",1000000d)
.andGreaterThan("porStock",20);
// 通过OR连接查询条件1--criteria1和查询条件2--criteria2
// porschExample就相当于select 字段名 from 表明 WHERE criteria 1
// 只需要调用or方法将criteria2连接上,就构成了完成的SQL语句
// select 字段名 from 表明 WHERE (criteria1) OR (criteria2)
porscheExample.or(criteria2);
List<Porsche> porscheList = porscheService.getPorschesByPriceAndStock(porscheExample);
for (Porsche porsche : porscheList) {
System.out.println("查询到的内容为:" + porsche);
}
}
复制代码
执行测试
3.1.2 QBC其他设置
QBC查询同时可以设置如排序、去重、设置查询的字段等,在getPorscheByPriceAndStock测试方法中增加相关代码,
porscheExample.setDistinct(true);
porscheExample.orderBy("porPrice").asc().orderBy("porName").desc();
porscheExample.selectProperties("porName","porPrice");
复制代码
再次执行测试 查看控制台删除的SQL语句,构建的复合条件在SQL执行时生效。
四、通用Mapper实现分页-RowBoundsMapper
4.1 分页相关方法
// 根据example条件和RowBounds进行分页查询
List<T> selectByExampleAndRowBounds(Object example, RowBounds rowBounds);
// 根据实体属性和RowBounds进行分页查询
List<T> selectByRowBounds(T record, RowBounds rowBounds);
复制代码
4.1.1 selectByRowBounds
在PorscheService接口中增加分页查询方法getPorschesByRowBounds
List<Porsche> getPorschesByRowBounds(Porsche record, RowBounds rowBounds);
复制代码
在PorscheServiceImpl中实现分页查询方法getPorschesByRowBounds
@Override
public List<Porsche> getPorschesByRowBounds(Porsche record, RowBounds rowBounds) {
return porscheMapper.selectByRowBounds(record,rowBounds);
}
复制代码
增加该方法的测试代码
@Test
public void getPorschesByRowBounds(){
int pageNo = 2;
int pageSize = 3;
int index = (pageNo - 1) * pageSize;
RowBounds rowBounds = new RowBounds(index, pageSize);
List<Porsche> porscheList = porscheService.getPorschesByRowBounds(null, rowBounds);
for (Porsche porsche : porscheList) {
System.out.println("查询到的内容为:" + porsche);
}
}
复制代码
执行测试
根据控制台输出的SQL语句,说明通用Mapper还是查询出所有的数据,在内存中进行的分页操作,”假分页“,不推荐使用这个分页查询,推荐使用PageHelper插件实现真正的分页
4.2 @Transient
一般情况下,实体类中的属性和数据库表中的字段都是一一对应的,但是有些属性可能在数据库中没有对应的字段,这时候就需要使用@Transient注解标明这不是表中的字段。
五、通用Mapper的逆向工程
5.1 通用Mapper的逆向工程
通用Mapper的逆向工程生成的代码与原生MyBatis生成的代码稍有区别,首先都包含Entity实体类、XxxMapper接口以及XxxMapper.xml,但是通用Mapper生成的Entity实体类中会包含如@Table注解、@Column注解、@Id注解以及@GeneratedValue等注解;生成的XxxMapper接口继承Mapper<Xxx>类;生成的XxxMapper.xml中没有SQL语句,只有一个resultMap标签。
5.2 使用通用Mapper的逆向工程
新建一个项目common-mapper-mbg,该项目的用到的依赖、Spring配置及MyBatis配置与general-mapper项目用到的一致,差异在于新建的general-mapper-mbg项目中不包含Entity实体类Mapper接口以及Service类和测试类等。
在common-mapper-mbg项目的pom.xml文件中增加通用Mapper的代码生成器依赖及maven插件,通过maven插件执行代码生成器生成代码,通过用Java代码或者命令行的方式也可运行代码生成器,这里不再赘述。
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-generator</artifactId>
<version>1.0.0</version>
</dependency>
复制代码
resources目录下新增generatorConfig.xml
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="db.properties"/>
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!--通用Mapper的MBG插件-->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
<property name="caseSensitive" value="true"/>
</plugin>
<jdbcConnection driverClass="${jdbc_driver}"
connectionURL="${jdbc_url}"
userId="${jdbc_username}"
password="${jdbc_password}">
</jdbcConnection>
<javaModelGenerator targetPackage="com.citi.entity"
targetProject="./src/main/java"/>
<sqlMapGenerator targetPackage="mappers"
targetProject="./src/main/resources"/>
<javaClientGenerator targetPackage="com.citi.mapper"
targetProject="./src/main/java"
type="XMLMAPPER"/>
<!--tableName="%"表示所有表都参与逆向工程-->
<table tableName="t_teacher" domainObjectName="Teacher">
<generatedKey column="id" sqlStatement="Mysql"/>
</table>
</context>
</generatorConfiguration>
复制代码
pom.xml中增加插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>
${basedir}/src/main/resources/generatorConfig.xml
</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
复制代码
运行通用Mapper的MBG
代码生成器运行成功
5.3 测试通用Mapper生成的代码
新建代码生成器生成的TeacherMapper的测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class TeacherMapperTest {
@Resource
private TeacherMapper teacherMapper;
@Test
public void selectAll(){
List<Teacher> teachers = teacherMapper.selectAll();
for (Teacher teacher : teachers) {
System.out.println("查询到的内容为:" + teacher.getTeacherName());
}
}
}
复制代码
执行该测试 控制台成功输出查询内容,通用Mapper的逆向工程生成的代码验证成功。