Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Specification for set contains operation

I have two entities, A and B. There is many-to-many relationship between them, where A hold a list of B. How to write a Specification to retrieve all A entites that contains B with specific name? Example:

@Service
public class YourService {

    @Resource
    private ARepository repository;

    // I know how to do this type of queries with specifications
    public List<A> getByB(B b) {
        return repository.findAll(Specifications.containsB(b));
    }

    //Question: how to write Specification for this type of query?
    public List<A> getByNameOfB(String name) {
         return repository.findAll(Specifications.containsBWithName(name));
    }
} 

Entities:

@Entity
public class B {

        @Id
        @SequenceGenerator(sequenceName = "B_SEQ", name = "BSeq", allocationSize = 1)
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BSeq")
        private Long id;

        @Column(unique = true, updatable = false)
        private String name;
}

@Entity
public class A {

    @Id
    @SequenceGenerator(sequenceName = "A_SEQ", name = "ASeq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ASeq")
    private Long id;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "A_B",
            joinColumns = {@JoinColumn(name = "A_ID", nullable = false, updatable = false)},
            inverseJoinColumns = {@JoinColumn(name = "B_ID", nullable = false, updatable = false)})
    @Fetch(value = FetchMode.SUBSELECT)
    private List<B> bList;
}

Metamodels:

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(A.class)
public class A_ {
    public static volatile ListAttribute<A, B> bList;
}


@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(B.class)
public class B_ {
    public static volatile SingularAttribute<B, String> name;
}

Retrieval:

public interface ARepository extends JpaRepository<A, Long>, JpaSpecificationExecutor<A> {
}

public class Specifications {

    public static Specification<A> containsB(B b) {
        return (root, query, cb) -> {
            Expression<List<B>> bList = root.get(A_.bList);
            return cb.isMember(b, bList);
        };
    }

    // HERE IS A QUESTION:

    public static Specification<A> containsBWithName(String name) {
        return (root, query, cb) -> {
            ListJoin<List<B>> bList = root.join(A_.bList);
            Expression<String> exp = bList.get(B_.name)
            //TODO how to check that name is one of the retrieved names?
            //PROBLEM, method below expects Expression<List<String>> instead of Expression<String>
            cb.isMember(name, exp); 
        };
    }
}
like image 620
VB_ Avatar asked Dec 02 '22 12:12

VB_


1 Answers

Try to do next:

public static Specification<A> containsBWithName(String name) {
    return (root, query, cb) -> {
        root.join("bList", JoinType.INNER);
        return cb.equal(root.get("bList").get("name"), name);
    };
}

Hope it will do the trick.

Spring Data version 1.11.4

like image 50
Andrew Nepogoda Avatar answered Mar 09 '23 17:03

Andrew Nepogoda