Hibernate @Where 注释语法问题

Posted

技术标签:

【中文标题】Hibernate @Where 注释语法问题【英文标题】:Hibernate @Where annotation syntax issue 【发布时间】:2020-02-29 22:43:16 【问题描述】:

我在使用 Where 注释时遇到了问题 我在 mysql 8.0.18 上有两个表,Venues 和 Concerts,具有这种结构和字段

CREATE TABLE VENUE
(
    ID          INT          NOT NULL AUTO_INCREMENT,
    NAME        VARCHAR(220) NOT NULL,
    IMAGE       VARCHAR(240),
    ACTIVE      BIT          NOT NULL DEFAULT 0,
    COORDINATES VARCHAR(120),
    COUNTRY     VARCHAR(120) NOT NULL DEFAULT 'USA',
    CITY        VARCHAR(220) NOT NULL DEFAULT 'None',
    RATING      INT          NOT NULL DEFAULT 0,
    VERSION     INT          NOT NULL DEFAULT 0,
    PRIMARY KEY (ID)
);

CREATE TABLE CONCERT
(
    ID          INT          NOT NULL AUTO_INCREMENT,
    NAME        VARCHAR(120) NOT NULL,
    DESCRIPTION VARCHAR(120) NOT NULL,
    FESTIVAL_ID INT,
    VENUE_ID    INT,
    RATING      INT          NOT NULL DEFAULT 0,
    DATE        DATETIME,
    IMAGE       BLOB,
    VERSION     INT          NOT NULL DEFAULT 0,
    FOREIGN KEY (FESTIVAL_ID) REFERENCES FESTIVAL (ID),
    FOREIGN KEY (VENUE_ID) REFERENCES VENUE (ID),
    PRIMARY KEY (ID)
);

Java Hibernate 和 Spring Data JPA 配置如下

package com.heatmanofurioso.gigger.service.persistencehibernateimplementation.config;

import com.heatmanofurioso.gigger.service.persistencehibernateimplementation.HibernateServiceMarker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@EnableJpaRepositories(basePackageClasses = HibernateServiceMarker.class)
@ComponentScan(basePackageClasses = HibernateServiceMarker.class)
@Slf4j
@EnableTransactionManagement
public class HibernateUtilConfig 

    private static final String MYSQL_DATA_SOURCE = "java:jboss/MysqlDataSource";
    private static final String HIBERNATE_ENTITIES = "com.heatmanofurioso.gigger.service.persistencehibernateimplementation.entity";

    @Bean
    public DataSource dataSource() 
        JndiDataSourceLookup jndiDataSourceLookup = new JndiDataSourceLookup();
        return jndiDataSourceLookup.getDataSource(MYSQL_DATA_SOURCE);
    

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() 
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan(HIBERNATE_ENTITIES);
        log.info("Created entity manager successfully");
        JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();

        //Properties to show SQL format of tables on deploy
        Properties jpaProperties = new Properties();
        jpaProperties.put("hibernate.show_sql", true); // Mark as true to log hibernate queries
        jpaProperties.put("hibernate.format_sql", true); // Mark as true to log hibernate queries
        entityManagerFactoryBean.setJpaProperties(jpaProperties);

        entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        return entityManagerFactoryBean;
    

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) 
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() 
        return new PersistenceExceptionTranslationPostProcessor();
    

package com.heatmanofurioso.gigger.service.persistencehibernateimplementation.dao;

import com.heatmanofurioso.gigger.service.persistencehibernateimplementation.entity.VenueEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface VenueHibernateServiceImpl extends JpaRepository<VenueEntity, Long> 
    @Query("SELECT t FROM VenueEntity t WHERE t.name like ?1")
    List<VenueEntity> getByName(String name);

    @Query("SELECT t FROM VenueEntity t WHERE t.coordinates = ?1")
    List<VenueEntity> getByCoordinates(String coordinates);

package com.heatmanofurioso.gigger.service.persistencehibernateimplementation.dao;

import com.heatmanofurioso.gigger.service.persistencehibernateimplementation.entity.ConcertEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ConcertHibernateServiceImpl extends JpaRepository<ConcertEntity, Long> 

    @Query("SELECT t FROM ConcertEntity t WHERE t.name like ?1")
    List<ConcertEntity> getByName(String name);

我正在使用 Java 11 Spring JPA 5.2.0.RELEASE 和 Hibernate 5.4.8 成功获取以下实体,这些实体部署在 Wildfly 18.0.0.Final 上,使用以下实体

package com.heatmanofurioso.gigger.service.persistencehibernateimplementation.entity;

import org.hibernate.annotations.Where;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "VENUE")
public class VenueEntity 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false)
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;

    @Column(name = "IMAGE", nullable = false)
    private String image;

    @Column(name = "ACTIVE", nullable = false)
    private Boolean active;

    @Column(name = "COORDINATES")
    private String coordinates;

    @Column(name = "RATING")
    private Long rating;

    @Column(name = "COUNTRY")
    private String country;

    @Column(name = "CITY")
    private String city;

    @OneToMany(mappedBy = "venue", fetch = FetchType.EAGER)
    private Set<ConcertEntity> concerts = new HashSet<>();

    @Column(name = "VERSION")
    @Version
    private Long version;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getImage() 
        return this.image;
    

    public void setImage(String image) 
        this.image = image;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Boolean getActive() 
        return active;
    

    public void setActive(Boolean active) 
        this.active = active;
    

    public String getCoordinates() 
        return coordinates;
    

    public void setCoordinates(String coordinates) 
        this.coordinates = coordinates;
    

    public Long getRating() 
        return rating;
    

    public void setRating(Long rating) 
        this.rating = rating;
    

    public String getCountry() 
        return country;
    

    public void setCountry(String country) 
        this.country = country;
    

    public String getCity() 
        return city;
    

    public void setCity(String city) 
        this.city = city;
    

    public Set<ConcertEntity> getConcerts() 
        return concerts;
    

    public void setConcerts(Set<ConcertEntity> concerts) 
        this.concerts = concerts;
    

    public Long getVersion() 
        return version;
    

    public void setVersion(Long version) 
        this.version = version;
    

package com.heatmanofurioso.gigger.service.persistencehibernateimplementation.entity;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "CONCERT")
public class ConcertEntity 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false)
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;

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

    @Column(name = "RATING")
    private Long rating;

    @Column(name = "DATE", nullable = false)
    private String date;

    @Column(name = "IMAGE")
    private String image;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "USER_CONCERT",
        joinColumns = @JoinColumn(name = "CONCERT_ID"),
        inverseJoinColumns = @JoinColumn(name = "USER_ID")
    )
    private Set<UserEntity> userConcert = new HashSet<>();

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "CONCERT_ARTIST",
        joinColumns = @JoinColumn(name = "CONCERT_ID"),
        inverseJoinColumns = @JoinColumn(name = "ARTIST_ID")
    )
    private Set<ArtistEntity> concertArtist = new HashSet<>();

    @OneToOne
    @JoinColumn(name = "FESTIVAL_ID", nullable = false)
    private FestivalEntity festival;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "VENUE_ID")
    private VenueEntity venue;

    @Column(name = "VERSION")
    @Version
    private Long version;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getDescription() 
        return description;
    

    public void setDescription(String description) 
        this.description = description;
    

    public Long getRating() 
        return rating;
    

    public void setRating(Long rating) 
        this.rating = rating;
    

    public LocalDateTime getDate() 
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return LocalDateTime.parse(date, formatter);
    

    public void setDate(LocalDateTime date) 
        this.date = date.toString();
    

    public String getDateString() 
        return date;
    

    public void setDate(String date) 
        this.date = date;
    

    public String getImage() 
        return image;
    

    public void setImage(String image) 
        this.image = image;
    

    public Set<UserEntity> getUserConcert() 
        return userConcert;
    

    public void setUserConcert(Set<UserEntity> userConcert) 
        this.userConcert = userConcert;
    

    public Set<ArtistEntity> getConcertArtist() 
        return concertArtist;
    

    public void setConcertArtist(Set<ArtistEntity> concertArtist) 
        this.concertArtist = concertArtist;
    

    public FestivalEntity getFestival() 
        return festival;
    

    public void setFestival(FestivalEntity festival) 
        this.festival = festival;
    

    public VenueEntity getVenue() 
        return venue;
    

    public void setVenue(VenueEntity venue) 
        this.venue = venue;
    

    public Long getVersion() 
        return version;
    

    public void setVersion(Long version) 
        this.version = version;
    

返回的数据集对应如下

现在,我的问题是,如果我尝试将 @Where 注释添加到我的 VenueEntity 上的 Concerts 集合中,就像这样


@OneToMany(mappedBy = "venue", fetch = FetchType.EAGER)
    private Set<ConcertEntity> concerts = new HashSet<>();

    @OneToMany(mappedBy = "venue", fetch = FetchType.EAGER)
    @Where(clause = "date < current_date")
    private Set<ConcertEntity> pastConcerts = new HashSet<>();

    @OneToMany(mappedBy = "venue", fetch = FetchType.EAGER)
    @Where(clause = "date => current_date")
    private Set<ConcertEntity> futureConcerts = new HashSet<>();

这两个集合返回空数据集,尽管在数据库上创建行时,我将DATE 字段的值设置为current_date,因此pastConcerts 集合至少应该显示结果

【问题讨论】:

【参考方案1】:

Borh Sets 是空的,可能是因为 current_datenull。在这种情况下,两个谓词都解析为false。发生这种情况是因为 hibernate 无法解析名为 current_date 的列并将其设置为 null。

您可以通过使用 subselect 来解决这个问题,因为 clause 只是一个 sql 谓词,它会在创建查询时粘贴到 where 中(这取决于您使用的数据库):

@Where(clause = "date < (select * from current_date())")

在某些数据库中或更短:

@Where(clause = "date < (select current_date())")

P.S.:在我看来,这不是一个好的设计。它更难测试,并且使与这些集合有关的每个查询都无法预测(相同的查询可能会根据运行时间产生不同的结果)。

【讨论】:

我尝试了你的方法,但是我得到了下面的 MySQL 错误错误是下面的 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的 'current_date())) 和 pastconcer0_.VENUE_ID=1' 附近使用正确的语法【参考方案2】:

我有多次映射的相同关联(字面意思是相同的 FK 列) - 但因为 Hibernate 知道这是同一个 FK,所以它认为它们是相同的。

在调试 Hibernate 生成的 SQL 时发现。

本质上,这在实体上是不可能的

【讨论】:

以上是关于Hibernate @Where 注释语法问题的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate @Where 未在 @MappedSuperClass 实体中强制执行

如何在 Spring JPARepository 中复制 Hibernate 的 @Where 功能

hibernate.cfg.xml放在哪

消息 -“无法读取 hi 值 - 您需要填充表:hibernate_sequence”

使用 Hibernate 使用外键 (where) 查询数据库表

[原创]java WEB学习笔记78:Hibernate学习之路---session概述,session缓存(hibernate 一级缓存),数据库的隔离级别,在 MySql 中设置隔离级别,在 Hi