springboot实战之ORM整合(JPA篇)
- 2019 年 10 月 4 日
- 筆記
前言
1、什么是ORM
对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。简单来说就是将数据库表与java实体对象做一个映射
2、ORM的优缺点
优点:符合面向对象编程;技术与业务解耦,开发时不需要关注数据库的连接与释放;
缺点:orm会牺牲程序的执行效率和会固定思维模式
3、orm主流框架
hibernate(jpa)、mybatis/mybatis-plus(半自动orm)。今天主要基于jpa规范再次封装抽象实现的SpringData JPA。在介绍SpringData JPA之前,先介绍一下jpa
什么是jpa
JPA是Java Persistence API的简称,中文名为Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
JPA包括以下3方面的内容:
(1)一套API标准。在javax.persistence的包下面,用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从烦琐的JDBC和SQL代码中解脱出来。
(2)面向对象的查询语言:Java Persistence QueryLanguage(JPQL)。这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
(3)ORM(object/relational metadata)元数据的映射。JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
demo实战
通过demo示例可以了解或者掌握以下内容
- 自动建表
- 建表引擎改为InnoDB
- 利用JpaSpecificationExecutor、JpaRepository来实现带复杂查询分页,以及常规增删改查
- 重写SimpleJpaRepository的save方法,使其按需更新空值属性
1、pom.xml引入相应包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
2、application.yml配置
spring: datasource: name: druidDataSource type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot-learning?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&serverTimezone=UTC username: root password: config.file=classpath:druid.properties filters: stat,log4j,config max-active: 100 initial-size: 1 max-wait: 60000 min-idle: 1 db-type: mysql time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: select 'x' test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-open-prepared-statements: 50 max-pool-prepared-statement-per-connection-size: 20 connection-properties: config.file=classpath:druid.properties # 启用加密,配置公钥。 filter: config: enabled: true jpa: show-sql: true hibernate: ddl-auto: update properties: hibernate.format_sql: true database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
jpa一些比较核心配置属性介绍
jpa.hibernate.ddl-auto参数的作用主要用于:自动创建|更新|验证数据库表结构。如果不是此方面的需求建议取值设为none
可选参数
- create 启动时删数据库中的表,然后创建,退出时不删除数据表
- create-drop 启动时删数据库中的表,然后创建,退出时删除数据表,如果表不存在报错
- update 如果启动时表格式不一致则更新表,原有数据保留
- validate 项目启动表结构进行校验 如果不一致则报错
spring.jpa.database-platform这个参数的主要用于指定默认的数据库存储引擎,在springboot2版本中,默认mysql数据库存储引擎的是MyISAM,通过把取值设置为org.hibernate.dialect.MySQL5InnoDBDialect,就可以把默认的存储引擎切换为InnoDB
3、创建entity
@Entity @Table(name="order_log") @AllArgsConstructor @NoArgsConstructor @Data @Builder @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @IgnoreNullValue public class OrderLog extends BaseEntity { @Column(name="order_id") private Long orderId; @Column(name="order_content",length = 2000) private String orderContent; @Column(name="order_name") private String orderName; } 父类 @Data @AllArgsConstructor @NoArgsConstructor @MappedSuperclass public class BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @CreationTimestamp @Column(name="create_date",updatable = false) private Date createDate; @UpdateTimestamp @Column(name="update_date") private Date updateDate; }
entity注解解释
- @Entity 声明类为实体或表。
- @Table 声明表名
- @Id 指定的类的属性,用于识别(一个表中的主键)。
- @GeneratedValue 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值
- @Column 指定持久属性栏属性。
- @MappedSupperclass 用来申明一个超类,继承这个类的子类映射时要映射此类中的字段
- @CreationTimestamp 数据库做插入时,自动填充时间
- @UpdateTimestamp 数据库有更新时,自动更新时间
本例只用一个entity来演示,因此没有涉及到表与表的关联,常用表与表之间的关联注解如下
- @JoinColumn 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。
- @ManyToMany 定义了连接表之间的多对多一对多的关系。
- @ManyToOne 定义了连接表之间的多对一的关系。
- @OneToMany 定义了连接表之间存在一个一对多的关系。
- @OneToOne 定义了连接表之间有一个一对一的关系。
4、创建Repository
通过继承JpaRepository可以实现增删改查,包括简单分页,通过继承JpaSpecificationExecutor可以实现复杂查询
public interface OrderLogRepository extends JpaSpecificationExecutor<OrderLog>,JpaRepository<OrderLog,Long> { }
在使用Repository存在一个坑点,更新时,调用其提供的save方法会导致null属性覆盖到数据库。即如果要更新的bean中的字段,存在null值,原生的SimpleJpaRepository进行更新操作时,会把null值更新进数据库,而有时候业务上我们不需要这样,因此可以重写SimpleJpaRepository方法,其核心代码如下
/** * 通用save方法 :新增/选择性更新 */ @Override @Transactional public <S extends T> S save(S entity) { //获取ID ID entityId = (ID) entityInformation.getId(entity); Optional<T> optionalT; if (entityId == null) { //标记为新增数据 optionalT = Optional.empty(); } else { //若ID非空 则查询最新数据 optionalT = findById(entityId); } //若根据ID查询结果为空 if (!optionalT.isPresent()) { em.persist(entity);//新增 return entity; } else { if(entity.getClass().isAnnotationPresent(IgnoreNullValue.class)){ IgnoreNullValue nullValue = entity.getClass().getAnnotation(IgnoreNullValue.class); if(nullValue.value()){ //1.获取最新对象 T target = optionalT.get(); //2.将非空属性覆盖到最新对象 BeanUtil.copyNotNUllProperties(entity,target); //3.更新非空属性 em.merge(target); return (S) target; }else{ em.merge(entity); } }else{ em.merge(entity); } return entity; } }
@IgnoreNullValue这个注解是用来指定是否要忽略空值字段。
在启动类上指定@EnableJpaRepositories注解,并将repositoryBaseClass设置为CustomSimpleJpaRepository,改成我们重写后的Repository,默认repositoryBaseClass为SimpleJpaRepository
@SpringBootApplication @EnableJpaRepositories(basePackages = {"com.github.lybgeek.orm.jpa.repository"},repositoryBaseClass = CustomSimpleJpaRepository.class) public class OrmApplication { public static void main( String[] args ) { SpringApplication.run(OrmApplication.class,args); } }
5、分页查询代码示例
public PageResult<OrderLog> pageOrderLogs(PageQuery<OrderLog> pageQuery) { Sort sort = new Sort(Direction.DESC,"createDate"); Pageable pageable = PageRequest.of(pageQuery.getPageNo() - 1,pageQuery.getPageSize(),sort); Specification<OrderLog> specification = (Specification<OrderLog>) (root, criteriaQuery, criteriaBuilder) -> { OrderLog queryParams = pageQuery.getQueryParams(); if(queryParams != null){ List<Predicate> predicates = new ArrayList<>(); if(ObjectUtils.isNotEmpty(queryParams.getOrderId())){ Path<Long> orderId = root.get("orderId"); Predicate orderIdPredicate = criteriaBuilder.equal(orderId,queryParams.getOrderId()); predicates.add(orderIdPredicate); } if(StringUtils.isNotBlank(queryParams.getOrderName())){ Path<String> orderName = root.get("orderName"); Predicate orderNamePredicate = criteriaBuilder.like(orderName,"%"+queryParams.getOrderName()+"%"); predicates.add(orderNamePredicate); } if(CollectionUtils.isNotEmpty(predicates)){ return criteriaBuilder.and(predicates.toArray(new Predicate[0])); } } return null; }; Page<OrderLog> orderLogPage = orderLogRepository.findAll(specification,pageable); if(ObjectUtils.isNotEmpty(orderLogPage)){ return PageUtil.INSTANCE.getPage(orderLogPage); } return null; }
文中相关概念引用文档
https://blog.csdn.net/u014131617/article/details/85813091 https://blog.csdn.net/xudailong_blog/article/details/84336629
总结
本文主要是介绍springdata jpa一些常规基本用法,只做入门,其具体更详细的内容,可以查看官网介绍
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
另外由于篇幅原因,其项目中包含的一些杂项诸如druid加密,flyway数据库版本管理,do和dto的相互转换,本文就不再论述,感兴趣的朋友可以查看我下边贴出来的demo链接。下篇会继续介绍mybatis、mybatisplus的基本使用
demo链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-orm