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