Spring Data JPA中踩過的坑


說老實話,Spring Data JPA很好用,上次使用還是2013年,當時只是完成Java Bean和數據庫中表的映射。

最近想起來用Spring Data JPA的起因是手頭有一個項目,源代碼是用原生SQL+JDBC實現的,在第一次部署時要初始化數據庫,還hardcode了很多數據庫配置參數。正好最近有空,就打算用Spring Boot Data JPA(spring-boot-starter-data-jpa)改造一下,仔細看了一下源代碼發現和幾年前已經天差地別,如果你的業務邏輯不是特別復雜或者表結構設計合理,一行真正SQL邏輯實現都不要寫,良心啊,真是為我這樣的懶人着想啊。

首先開始寫Repository,常見的用法有兩種,一個是寫一個接口繼承JpaRepository,代碼如下:

package com.company.inventory.repository;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import comcompany.inventory.model.Device;
public interface DeviceRepository extends JpaRepository<Device, Long> {
    List<Device> findBySnOrderByGmtCreatedDesc(String sn);
}

另外一個是繼承CrudRepository,代碼如下:

package com.company.inventory.repository;

import java.util.List;
import org.springframework.data.repository.CrudRepository;
import comcompany.inventory.model.Device;
public interface DeviceRepository extends CrudRepository<Device, Long> {
    List<Device> findBySnOrderByGmtCreatedDesc(String sn);
}

JpaRepository和CrudRepository關系如下:

 

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> 
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> 

public interface CrudRepository<T, ID> extends Repository<T, ID>

從上面的關系就能看出JpaRepository除了能做CrudRepository能做的所有事外,還多了分頁和排序功能以及QueryByExampleExecutor提供的QueryByExample功能。但是JpaRepository也和JPA持久化技術進行了綁定。http://jtuts.com/2014/08/26/difference-between-crudrepository-and-jparepository-in-spring-data-jpa/

所以建議盡量使用CrudRepository或者PagingAndSortingRepository

Supported keywords inside method names

Keyword

Sample

JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,

findByFirstnameIs,

findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1(parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1(parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1(parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

 接下來是定義Entity,遇到了第一個坑,代碼如下:

package com.company.inventory.model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table( name = "device" )
public class Device implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    @Column(unique = true, nullable = false)
    private String sn;
    @Column(name = "gmtCreated", nullable = false)
    private Date gmtCreated = new Date();
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Date getGmtCreated() {
        return gmtCreated;
    }
    public void setGmtCreated(Date gmtCreated) {
        this.gmtCreated = gmtCreated;

    } 

 }

第一個坑,該類必須是實體類,不能繼承於任何基類,除非其中的參數不需要映射到數據庫中。 

例如有一個基類如下:

public class BaseModel{

    protected String name;

    //set, get方法忽略 

public class Person extends BaseModel  implements Serializable{

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)

   private Long id; 

name將不能映射到數據庫中。

第二個坑,如果Field類似gmtCreated,有大寫字母的,默認映射到數據庫字段名會變為gmt_created。如果你的設計不是這樣,而是直接映射不做修改。需要在application.properties中指定Naming strategy,否則默認是org.hibernate.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy,在進行領域映射時,首字母小寫,大寫字母變為下划線加小寫。添加代碼如下:

spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 

第三個坑,如果使用like,代碼如下:

public Page<User> findUserByKey(String key, int page, int size){
    Page<User> result = userRepository.findAll(new Specification<User> () {  
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {  
                Join<User, Role> join = root.join("role", JoinType.INNER);
                Path<String> namePath = join.get("name");  
                Path<String> statusPath = root.get("status");  
                Path<String> usernamePath = root.get("username"); 
                Path<String> nicknamePath = root.get("nickname");
                Predicate or = cb.or(cb.like(namePath, key), cb.like(usernamePath, key), cb.like(nicknamePath, key));
                return cb.and(or, cb.equal(statusPath, 0));   
            }  
        },  PageRequest.of(page - 1, size));
        return result;
    }

其中的key,必須是類似%abc%,而不是'%abc%'。
'

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM