简体   繁体   中英

Spring Data JPA find by embedded object property

I want to write a Spring Data JPA repository interface method signature that will let me find entities with a property of an embedded object in that entity. Does anyone know if this is possible, and if so how?

Here's my code:

@Entity
@Table(name = "BOOK_UPDATE_QUEUE", indexes = { uniqueConstraints = @UniqueConstraint(columnNames = {
        "bookId", "region" }, name = "UK01_BOOK_UPDATE_QUEUE"))
public class QueuedBook implements Serializable {

    @Embedded
    @NotNull
    private BookId bookId;

    ...

}

@Embeddable
public class BookId implements Serializable {

    @NotNull
    @Size(min=1, max=40)
    private String bookId;

    @NotNull
    @Enumerated(EnumType.STRING)
    private Region region;

    ...

}

public interface QueuedBookRepo extends JpaRepository<QueuedBook, Long> {

    //I'd like to write a method like this, but can't figure out how to search by region,
    //when region is actually a part of the embedded BookId
    Page<QueuedBook> findByRegion(Region region, Pageable pageable);

}

Can I write a query for this using Spring Data?

This method name should do the trick:

Page<QueuedBook> findByBookIdRegion(Region region, Pageable pageable);

More info on that in the section about query derivation of the reference docs.

The above - findByBookIdRegion() did not work for me. The following works with the latest release of String Data JPA:

Page<QueuedBook> findByBookId_Region(Region region, Pageable pageable);

If you are using BookId as an combined primary key, then remember to change your interface from:

public interface QueuedBookRepo extends JpaRepository<QueuedBook, Long> {

to:

public interface QueuedBookRepo extends JpaRepository<QueuedBook, BookId> {

And change the annotation @Embedded to @EmbeddedId, in your QueuedBook class like this:

public class QueuedBook implements Serializable {

@EmbeddedId
@NotNull
private BookId bookId;

...

According to me, Spring doesn't handle all the cases with ease. In your case the following should do the trick

Page<QueuedBook> findByBookIdRegion(Region region, Pageable pageable);  

or

Page<QueuedBook> findByBookId_Region(Region region, Pageable pageable);

However, it also depends on the naming convention of fields that you have in your @Embeddable class,

eg the following field might not work in any of the styles that mentioned above

private String cRcdDel;

I tried with both the cases (as follows) and it didn't work (it seems like Spring doesn't handle this type of naming conventions(ie to many Caps , especially in the beginning - 2nd letter (not sure about if this is the only case though)

Page<QueuedBook> findByBookIdCRcdDel(String cRcdDel, Pageable pageable); 

or

Page<QueuedBook> findByBookIdCRcdDel(String cRcdDel, Pageable pageable);

When I renamed column to

private String rcdDel;

my following solutions work fine without any issue:

Page<QueuedBook> findByBookIdRcdDel(String rcdDel, Pageable pageable); 

OR

Page<QueuedBook> findByBookIdRcdDel(String rcdDel, Pageable pageable);

There are different ways of doing this.

  1. Using derived methods .
  2. Using native queries .
  3. Use the specification interface to construct complex queries.

Below are code snippet showing how you can query data using embedded properties.

The repository extends the JpaSpecificationExecutor interface.

public interface QuedBookRepository extends JpaRepository<QueuedBook, Long>, JpaSpecificationExecutor<QueuedBook>{
    //Using derived query methods
    Page<QueuedBook> findByBookId_Region(Region region, Pageable pageable);


    //Using native queries
    @Query("SELECT qb FROM QueuedBook qb "
        +"WHERE qb.bookId.region = :region")
    Page<QueuedBook> findByBookIdRegion(Region region, Pageable pageable);
    
    
    //Using specification interface
    static Specification<QueuedBook> findByBookIdRegion(Region region){
        return (root, query, criteriaBuilder) -> criteriaBuilder.equals(root.get("bookId").get("region"), region);
    }
}

To use the repository methods, access them as follows.

//In your service methods

Region region = ....;
Pageable pageable = ....;


//Using derived query methods
Page<QueuedBook> booksPage = repository.findByBookId_Region(region, pageable);

//Using native queries
Page<QueuedBook> booksPage = repository.findByBookIdRegion(region, pageable);

//Using specification interface
Specification<QueuedBook> spec = QuedBookRepository.findByBookIdRegion(region);
Page<QueuedBook> booksPage = repository.findAll(spec, pageable);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM