重学Springboot系列之整合数据库开发框架---中
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重学Springboot系列之整合数据库开发框架---中相关的知识,希望对你有一定的参考价值。
重学Springboot系列之整合数据库开发框架---中
java bean的赋值转换
为什么要做java bean赋值转换
在实际的开发过程中,由于业务的复杂性,通常并不能做到一个model实体贯穿持久层、服务层、控制层。通常需要进行实体对象java bean的赋值转换。
PO: persistent object 持久对象,对应数据库中的entity。通常在进行数据库数据存取操作时使用。可以简单的认为一个PO对应数据库中一张表中的一个记录。PO对象里面只有基本数据类型和String类型的属性(如:int、String),与数据库字段是一一对应的。
BO: business object 业务对象,业务对象主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。通常一个BO是多个PO的组合体,比如:PO在查询出来之后,需要经过业务处理,处理过程中对象的属性逐渐变复杂,有嵌套的数组,对象数组等等。
VO: view object,主要与web页面的展示结构相对应,所以VO也是前端与后端的数据交换定义。
下图中是一个VO,用于返回给前端web界面,用于渲染的数据内容:
下图是一个PO,用于对数据库表的数据的存取。
大家注意看二者的区别,一个AricleVO不仅包含了Article的数据,还包含了Reader读者的数据。
- 当你需要向数据库里面插入数据的时候,你需要将Article(PO)和Reader(PO)分别作为PO记录插入数据库。
- 当你需要将一篇文章的数据和读者信息返回给页面做渲染的时候,你需要从数据库里面查询Article(PO)和Reader(PO),然后将二者组合映射转换为AricleVO返回给前端。
如果你的业务,可以用一个实体类对象,就可以贯穿持久层到展现层,就没有必要做映射赋值转换,也没有必要去分VO、BO、PO。比如:单表表格数据展现、修改、新增。
BeanUtils和Dozer?
比较常用的java bean赋值转换工具是BeanUtils和Dozer,如果没有BeanUtils和Dozer帮我们进行对象之间的转换赋值,我们会怎么做?
articleVO.setId(article.getId());
articleVO.setAuthor(article.getAuthor());
articleVO.setTitle(article.getTitle());
articleVO.setContent(article.getContent());
articleVO.setCreateTime(article.getCreateTime());
BeanUtils是Spring Boot内自动集成的java bean自动转换工具(apache项目下也有一个BeanUtils,这里专指Spring包下面的BeanUtils),使用非常方便。可以通过下面的方法将article(PO) 转换为articleVO。
ArticleVO articleVO = new ArticleVO();
BeanUtils.copyProperties(article,articleVO);
dozer是一个能把实体和实体之间进行转换的工具.只要建立好映射关系.就像是ORM的数据库和实体映射一样。dozer的功能比BeanUtils功能更强大,但是BeanUtils的性能更好。所以简单的同名同类型属性赋值转换使用BeanUtils,复杂的级联结构的属性赋值转换使用Dozer
- Dozer可以实现Integer、Long等基础类型与String数据类型的属性之间的转换(只要名字相同就可以了,数据类型可以不同),BeanUtils只能做到同数据类型同名的属性之间赋值。
- Dozer可以实现递归级联结构的对象赋值,BeanUtils(Spring包下面的)也可以
- Dozer可以实现复杂的数据转换关系,通过xml配置的方式,BeanUtils做不到
使用方法示例如下:
Mapper mapper = DozerBeanMapperBuilder.buildDefault();
// article(PO) -> articleVO
ArticleVO articleVO = mapper .map(article, ArticleVO.class);
这段示例代码。将从数据库里面查询得到的PO对象article,转换为VO对象articleVO,转换过程将所有同名同类型的数据自动赋值给articleVO的成员变量,当然除了reader(因为PO里面没有reader数组数据)。转换需要写属性之间的映射么?不! 默认是根据属性名称来匹配的.
引入Dozer(6.2.0)
从6.2.0版本开始,dozer官方为我们提供了dozer-spring-boot-starter,这样我们在spring boot里面使用dozer更方便了。
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-spring-boot-starter</artifactId>
<version>6.2.0</version>
</dependency>
在实际开发中,我们不只需要PO转VO,有时还需要List转List.写一个工具类,实现List转List
public class DozerUtils
static Mapper mapper = DozerBeanMapperBuilder.buildDefault();
public static <T> List<T> mapList(Collection sourceList, Class<T> destinationClass)
List destinationList = new ArrayList();
for (Iterator i$ = sourceList.iterator(); i$.hasNext();)
Object sourceObject = i$.next();
Object destinationObject = mapper.map(sourceObject, destinationClass);
destinationList.add(destinationObject);
return destinationList;
自定义类型转换(非对称类型转换)
在平时的开发中,我们的VO和PO的同名字段尽量是类型一致的。String属性->String属性,Date属性 -> Date属性,但是也不排除有的朋友由于最开始的设计失误
- 需要String属性 -> Date属性,或者ClassA转ClassB呢?这种我们该如何实现呢?
- 或者需要createDate 转 cDate这种属性名称都不一样的,怎么做。
比如下面的两个测试model,进行属性自动赋值转换映射。
@Data
@AllArgsConstructor
public class TestA
public String name;
public String createDate; //注意这里名称不一样,类型不一样
@Data
@NoArgsConstructor
public class TestB
public String name;
public Date cDate; //注意这里名称不一样,类型不一样
然后,我们需要自己去创建转换对应关系,比如:resources/dozer/dozer-mapping.xml。xml内容看上去复杂,其实核心结构很简单。就是class-a到classb的转换,filed用来定义特殊字段(名称或类型不一致)。configuration可以做全局的配置,date-format对所有的日期字符串转换生效。
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping
http://dozermapper.github.io/schema/bean-mapping.xsd">
<configuration>
<date-format>yyyy-MM-dd HH:mm:ss</date-format>
</configuration>
<mapping>
<class-a>com.dhy.bootlaunch.dozer.TestA</class-a>
<class-b>com.dhy.bootlaunch.dozer.TestB</class-b>
<field>
<a>createDate</a>
<b>cDate</b>
</field>
</mapping>
</mappings>
然后把dozer转换配置文件通知application.yml,进行加载生效
dozer:
mapping-files: classpath:/dozer/dozer-mapping.xml
这样一个对象里面有String属性到Date属性转换的时候,就会自动应用这个转换规则, 不再报错。
@RunWith(SpringRunner.class)
@SpringBootTest
public class DozerTests
@Test
public void dozerTests()
Mapper mapper = DozerBeanMapperBuilder
.create().withMappingFiles("dozer/dozer-mapping.xml")
.build();
TestA testA = new TestA("kobe","2020-03-08 11:25:25");
System.out.println(mapper.map(testA,TestB.class));
输出:
TestB(name=kobe, cDate=Sun Mar 08 11:25:25 CST 2020)
映射localDateTime的问题
net.sf.dozer这个依赖的dozer转换LocalDateTime会出错,但是用com.github.dozermapper这个dozermapper就不会出问题,杠杆亲测,可以正常映射
整合MybatisGenerator操作数据
为了增强Mybatis的功能性和易用性,有两种比较常用的方案
- Mybatis Genenrator
- Mybatis Plus
我们本小节为大家介绍Mybatis Genenrator 的核心用法,下一节为大家介绍Mybatis Plus。
整合Mybatis
第一步:引入maven依赖包,包括mybatis相关依赖包和mysql驱动包。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
第二步:保证application.yml里面有数据库连接的配置。并配置mybatis的xml文件存放位置,下文配置的xml文件目录位置是resources/generator。
spring:
datasource:
url: jdbc:mysql://192.168.161.3:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: test
password: 4rfv$RFV
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:generator/*.xml
logging:
level:
com.dhy.bootlaunch: debug
- mybatis.mapper-locations表示去哪里扫描xml文件
第三步:配置Mybatis的Mapper类文件的包扫描路径
@SpringBootApplication
@MapperScan(basePackages = "com.dhy.boot.launch.generator")
public class BootLaunchApplication
public static void main(String[] args)
SpringApplication.run(BootLaunchApplication.class, args);
安装Mybatis generator插件
Mybatis Generator可以自动的帮助我们根据数据库表结构生成持久层的代码,能很大层度上帮助我们提高开发效率。Mybatis generator的使用方法有很多种,比如:
- XML配置文件实现Mybatis Generator代码生成配置
- 编码实现Mybatis Generator代码生成配置
- 通过IDEA插件实现Mybatis Generator代码生成配置
其中最简单易用的就是Mybatis Generator的IDEA插件来生成代码,直观、简单、易用。其实Mybatis Generator插件有很多,笔者就为大家介绍我最常使用的一个:better-mybatis-generator
(免费开源好用)
这个插件将帮助我们根据数据库表结构生成Mybatis操作接口及实体类定义等内容。能极大的方便我们开发,减少手写代码量。
插件怎么安装、如何使用,请点这里?
mybatis代码生成配置详解:
通过该插件,可以帮助我们自动生产Mybatis持久层代码。代码生成完成之后,我们直接使用就可以了。
增删改查实现代码
Service层接口
public interface ArticleRestService
void saveArticle(ArticleVO article);
void deleteArticle(Long id);
void updateArticle(ArticleVO article);
ArticleVO getArticle(Long id);
List<ArticleVO> getAll();
Service接口实现
@Service
public class ArticleMybatisRestService implements ArticleRestService
@Resource
protected Mapper dozerMapper;
@Resource
private ArticleMapper articleMapper; //由mybatis generator 帮我们自动生成的代码
//新增
@Override
public void saveArticle(ArticleVO article)
Article articlePO = dozerMapper.map(article,Article.class);
articleMapper.insert(articlePO); //该方法由自动代码生成提供
//删除
@Override
public void deleteArticle(Long id)
articleMapper.deleteByPrimaryKey(id); //该方法由自动代码生成提供
//更新
@Override
public void updateArticle(ArticleVO article)
Article articlePO = dozerMapper.map(article,Article.class);
articleMapper.updateByPrimaryKeySelective(articlePO); //该方法由自动代码生成提供
//查询
@Override
public ArticleVO getArticle(Long id)
//selectByPrimaryKey方法由自动代码生成提供
return dozerMapper.map(articleMapper.selectByPrimaryKey(id),ArticleVO.class);
//查询所有
@Override
public List<ArticleVO> getAll()
List<Article> articles = articleMapper.selectByExample(null); //该方法由自动代码生成提供
return DozerUtils.mapList(articles,ArticleVO.class);
测试一下
根据Service层函数参数,修改一下控制层Controller代码,使用postman测试一下接口的可用性。
附录:自动生产代码使用说明
使用代码生成工具之后,可以看到它帮助我们自动生成了四种文件:(Xxxxxx为代指,对应数据库表名。如表名叫message,则Xxxxxx代指Message)
- XxxxxxMapper.java,持久层api操作接口
- XxxxxxMapper.xml ,动态sql配置文件
- Xxxxxx的实体类,POJO,Java bean,与数据库表字段一一对应
- XxxxxxExample,数据库单表操作模板,Example可以理解为“条件”。可以作为"查询条件","更新条件“,”删除条件“!
开发规范:
- 自动生成的代码及所在的文件不允许修改,因为数据库可能变化重新生成,导致修改部分的代码丢失。
- 另外数据库表需要设置主键,mysql通常设置id为主键,自增。否则生成的代码及方法数量会减少。
public interface MessageMapper
//根据"条件"做count(*)
int countByExample(MessageExample example);
//根据"条件"删除记录
int deleteByExample(MessageExample example);
//根据表主键删除记录
int deleteByPrimaryKey(Long id);
//插入一条完整记录,record对象的所有属性都将插入数据库
int insert(Message record);
//插入一条记录,只插入record对象中不为空的属性。
int insertSelective(Message record);
//查询符合"条件"的对象列表
List<Message> selectByExample(MessageExample example);
//根据主键查询对象
Message selectByPrimaryKey(Long id);
//根据example将record中不为空的属性更新到数据库中
int updateByExampleSelective(@Param("record") Message record, @Param("example") MessageExample example);
//根据example将record中所有属性更新到数据库中(所有值覆盖)
//一旦record属性为空,对应的数据库字段不允许为空,则异常
int updateByExample(@Param("record") Message record, @Param("example") MessageExample example);
//根据主键将record中不为空的属性更新到数据库中
int updateByPrimaryKeySelective(Message record);
//根据主键将record中所有属性更新到数据库中(所有值覆盖)
int updateByPrimaryKey(Message record);
如何使用Example做sql操作?,Example是条件查询的意思
自动生成的代码比较适合单表简单的sql操作。
- 不适用于多表关联查询,
- 不建议用于带OR的,带IN的,带Exists关系的sql处理。
增加insert
创建增加的对象,并设置要增加对象的内容
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setUserName(userName);
int count = userMapper.insertSelective(sysUser);
写了上面的代码,就不用我们去写SQL操作数据库了。起同样作用的sql如下:
INSERT INTO sys_user (user_id,user_name)
VALUES (#userId,#userName);
注意 xxxxmapper中有两个insert方法,其中insertSelective是选择性插入,即:字段有值插入,空字段不做sql插入处理。sys_user表除了user_id,user_name还有其他字段,但是没有作为insert的字段出现。
删除Delete
创建要删除的模板,并设置删除条件
SysUserExample userExample = new SysUserExample();
userExample.createCriteria().andUserIdEqualTo(userId)
.andUserNameEqualTo(userName);
int count = userMapper.deleteByExample(userExample )
写了上面的代码,就不用我们去写SQL操作数据库了。起同样作用的sql如下:
DELETE FROM sys_user
WHERE user_id = #userId
AND user_name = #userName;
修改update
创建修改的对象,并设置要修改对象的内容
SysUser sysUser = new SysUser();
sysUser.setUserName(userName);
创建要修改条件的模板,并设置修改的条件
SysUserExample userExample = new SysUserExample();
userExample.createCriteria().andUserIdEqualTo(userId);
int count = userMapper.updateByExample(sysUser,userExample);
写了上面的代码,就不用我们去写SQL操作数据库了。起同样作用的sql如下:
update sys_user
set user_name = #userName
where user_id = #userId
简单查询
创建查询的模板并设置查询的条件
SysUserExample userExample = new SysUserExample();
userExample.createCriteria().andUserIdEqualTo(userId);
//根据查询获取登录人信息
SysUser myself = userMapper.selectByExample(userExample).get(0);
写了上面的代码,就不用我们去写SQL操作数据库了。起同样作用的sql如下:
SELECT id,user_id,user_name,`password`,org_id,role_id, phone,address
FROM sys_user
WHERE user_id = ?;
整合mybatisPlus操作数据库
SpringBoot集成MybatisPlus
第一步:通过maven坐标将mybatis-plus-boot-starter以及数据库驱动引入到Spring Boot项目里面来。注意:引入mybatis-plus-boot-starter的项目就不需要引入mybatis-spring-boot-starter了
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId重学Springboot系列之整合数据库开发框架---下