前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot实战之ORM整合(JPA篇)

springboot实战之ORM整合(JPA篇)

作者头像
lyb-geek
发布2019-09-16 16:06:01
5.6K0
发布2019-09-16 16:06:01
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

前言

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引入相应包

代码语言:javascript
复制
<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配置

代码语言:javascript
复制
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

代码语言:javascript
复制
@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可以实现复杂查询

代码语言:javascript
复制
public interface OrderLogRepository extends JpaSpecificationExecutor<OrderLog>,JpaRepository<OrderLog,Long> {

}

在使用Repository存在一个坑点,更新时,调用其提供的save方法会导致null属性覆盖到数据库。即如果要更新的bean中的字段,存在null值,原生的SimpleJpaRepository进行更新操作时,会把null值更新进数据库,而有时候业务上我们不需要这样,因此可以重写SimpleJpaRepository方法,其核心代码如下

代码语言:javascript
复制
/**
     * 通用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

代码语言:javascript
复制
@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、分页查询代码示例

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linyb极客之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 什么是jpa
  • demo实战
  • 文中相关概念引用文档
  • 总结
  • demo链接
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档