Hibernate / JPA многие ко многим объединяют таблицы и добавляют как несколько строк в качестве предиката - PullRequest
0 голосов
/ 01 мая 2019

Я пытаюсь создать поиск, в котором я выбираю строки из изображения с условиями для столбцов в таблице изображений и ключевые слова / теги из другой таблицы, сопоставленной с помощью отношения «многие ко многим».Изображение может иметь несколько тегов, а теги могут быть связаны с несколькими изображениями.

Таблицы:

**Image**
imageId
make
model
and so on....

**ImageTags**
imageId
tagId

**Tags**
tagId
tagName

Скажем, я хочу найти изображения с тегом "nature", SQL будет выглядеть так:

SELECT DISTINCT i.* FROM Images i
INNER JOIN ImagesTags it ON it.imageId = i.imageId
INNER JOIN Tags t ON t.tagId = it.tagId
WHERE t.tagName in ("city", "nature");

Может быть больше идругие условия в предложении WHERE для других столбцов таблицы Image, и поэтому я попытался построить динамические запросы с помощью API Criteria, поскольку условия будут смещаться при каждом запросе.Но я не могу заставить его работать.

Мне удалось только найти ключевое слово:

    @Transactional
    public List<Image> findByKeyword(String [] tags) {
        List<Image> images = null;
        try (Session session = entityManager.unwrap(Session.class)){
            String hql = "select distinct i from Image i " +
                    "join i.tags t " +
                    "where t.tagName in (:tags)";
            Query query = session.createQuery(hql);
            ((org.hibernate.query.Query) query).setParameterList("tags", tags);
            images = query.getResultList();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return images;
    }

Теперь я хочу добавить дополнительные условия в предложение WHERE, например:

SELECT DISTINCT i.* FROM Images i
INNER JOIN ImagesTags it ON it.imageId = i.imageId
INNER JOIN Tags t ON t.tagId = it.tagId
WHERE i.make = "Nikon" AND t.tagName in ("city", "nature");

Я пытался создать построитель и добавить предикаты, но не могу найти способ создания объединения.

CriteriaBuilder cBuilder = session.getCriteriaBuilder();
CriteriaQuery<Image> criteriaQuery = cBuilder.createQuery(Image.class);
Root<Image> imageRoot = criteriaQuery.from(Image.class);

predicateList.add(cBuilder.equal(imageRoot.get("imageId"), sc.getImageId()));
predicateList.add(cBuilder.equal(imageRoot.get("licenseType"), sc.getLicenseType()));

//more predicates are omitted

//Here somewhere I want to add an array or list of tags to the where clause and match that to the many-to-many relationship.

Predicate finalPredicate = cBuilder.and(predicateList.toArray(new Predicate[0]));
criteriaQuery.where(finalPredicate).distinct(true);
org.hibernate.query.Query<Image> imageQuery = session.createQuery(criteriaQuery);

List<Image> images = imageQuery.getResultList();

Класс изображения:

@Entity
@Table(name = "Images")
public class Image implements File {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "imageId")
    private int imageId;

    @Column(name = "fileName")
    private String fileName;

    @Column(name = "filePath")
    private String filePath;

    @Column(name = "description")
    private String description;

    @Column(name = "fileSize")
    private String fileSize;

    @Column(name = "dateTime")
    private LocalDateTime dateTime;

//Other attributes omitted

    @ManyToMany
    @JoinTable(
            name = "ImagesTags",
            joinColumns = @JoinColumn(name = "imageId"),
            inverseJoinColumns = @JoinColumn( name = "tagId"))
    private Set<Tag> tags;

//getter and setters omitted

Класс тегов:

@Entity
@Table(name = "Tags")
public class Tag {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "tagId")
    private int tagId;

    @Column(name = "tagName")
    private String tagName;

    @ManyToMany
    @JoinTable(
            name = "ImagesTags",
            joinColumns = @JoinColumn(name = "tagId"),
            inverseJoinColumns = @JoinColumn( name = "imageId"))
    private List<Image> images;

Как мне создать это объединение и добавить ключевые слова в предложение "WHERE tagName IN"?

Редактировать:

Я изменил код на этот, но он все равно не работает

CriteriaBuilder cBuilder = session.getCriteriaBuilder();
CriteriaQuery<Image> criteriaQuery = cBuilder.createQuery(Image.class);
Root<Image> imageRoot = criteriaQuery.from(Image.class);
Root<Tag> tagRoot = criteriaQuery.from(Tag.class);
Join<Image, Tag> join = imageRoot.join(Image_.TAGS);

List<Predicate> predicateList = new ArrayList<>();
predicateList.add(cBuilder.equal(imageRoot.get("width"), 2000));
predicateList.add(cBuilder.in(join));
predicateList.add(cBuilder.equal(tagRoot.get("tagName"), tags[0]));

Predicate finalPredicate = cBuilder.and(predicateList.toArray(new Predicate[0]));
criteriaQuery.select(imageRoot);
criteriaQuery.where(finalPredicate).distinct(true);
org.hibernate.query.Query<Image> imageQuery = session.createQuery(criteriaQuery);

List<Image> images = imageQuery.getResultList();

Редактировать 2

Попытка с этим:

CriteriaBuilder cBuilder = session.getCriteriaBuilder();
CriteriaQuery<Image> criteriaQuery = cBuilder.createQuery(Image.class);
Root<Image> imageRoot = criteriaQuery.from(Image.class);
Root<Tag> tagRoot = criteriaQuery.from(Tag.class);
Join<Image, Tag> join = imageRoot.join(Image_.TAGS);

List<Predicate> predicateList = new ArrayList<>();

predicateList.add(cBuilder.in(join));
predicateList.add(cBuilder.or(cBuilder.equal(tagRoot.get("tagName"), "träd")));

Predicate finalPredicate = cBuilder.and(predicateList.toArray(new Predicate[0]));
criteriaQuery.select(imageRoot);
criteriaQuery.where(finalPredicate).distinct(true);
org.hibernate.query.Query<Image> imageQuery = session.createQuery(criteriaQuery);

List<Image> images = imageQuery.getResultList();

Но это производит (для меня) очень странный запрос к базе данных:

select distinct generatedAlias0 
from Image as generatedAlias0,
     Tag as generatedAlias1 
inner join generatedAlias0.tags as generatedAlias2 
where (generatedAlias2 in ()) and (
            generatedAlias1.tagName=:param0 
        ) 
select distinct image0_.imageId as imageId1_1_,
            image0_.author as author16_1_,
            image0_.dateTime as dateTime2_1_,
            image0_.description as descript3_1_,
            image0_.fileName as fileName4_1_,
            image0_.filePath as filePath5_1_,
            image0_.fileSize as fileSize6_1_,
            image0_.height as height7_1_,
            image0_.licenseType as licenseT8_1_,
            image0_.location as location9_1_,
            image0_.make as make10_1_,
            image0_.model as model11_1_,
            image0_.noOfAllowedUses as noOfAll12_1_,
            image0_.price as price13_1_,
            image0_.resolution as resolut14_1_,
            image0_.width as width15_1_ 
        from
            Images image0_ 
        inner join
            ImagesTags tags2_ 
                on image0_.imageId=tags2_.imageId 
        inner join
            Tags tag3_ 
                on tags2_.tagId=tag3_.tagId cross 
        join
            Tags tag1_ 
        where(tag3_.tagId in ()) and tag1_.tagName=?
...