Spring Boot JPA不会将模式名称添加到查询中的表中

时间:2017-11-14 23:08:29

标签: hibernate jpa spring-boot

根据我的理解(例如,来自here),如果我为我的实体指定了架构,那么在创建查询时它应该使用该架构名称。

所以,如果我有一个实体:

@Entity
@Table(name="proposalstatuses",schema="sales")
public class ProposalStatus implements Serializable {
    private static final long serialVersionUID = 1L;
    private int proposalStatusID;
    private String proposalStatusName;

    public ProposalStatus() {}

    public ProposalStatus(String proposalStatusName) {
        this.proposalStatusName = proposalStatusName;
    }

    @Id
    @Column(name="pk_proposalstatusid")
    @GeneratedValue(strategy = GenerationType.AUTO)
    public int getProposalStatusID() {
        return proposalStatusID;
    }

    public void setProposalStatusID(int proposalStatusID) {
        this.proposalStatusID = proposalStatusID;
    }

    @Column(name="proposalstatusname", unique=true, nullable=false)
    public String getProposalStatusName() {
        return proposalStatusName;
    }

    public void setProposalStatusName(String proposalStatusName) {
        this.proposalStatusName = proposalStatusName;
    }
}

然后我希望Hibernate生成像select ... from sales.proposalstatuses这样的查询。但是,相反,我看到了:

select proposalst0_.pk_proposalstatusid as pk_propo1_8_, proposalst0_.proposalstatusname as proposal2_8_ 
from proposalstatuses proposalst0_ 
order by proposalst0_.proposalstatusname asc

对于这种情况,这不是一个大问题,但现在我希望能够在不同的模式中使用与表的连接,这是失败的,因为它认为表不存在(并且它们不存在在默认架构中。)

因此,我有一个具有多个架构的数据库(一个连接)。如何让Hibernate在引用表时使用模式名称?看起来应该非常直截了当,但我必须遗漏一些东西 谢谢!

我正在使用Spring Boot 1.5.7,它使用Hibernate JPA 2.1和Hibernate Core 5.0.12。 如果我使用H2数据源,这确实有效。如果MySQL是数据源,我只会看到一个问题。

我已经阅读了有关在我想要引用的表的默认架构中创建视图的信息。但是,这不是一个可行的选择,因为我必须创建大量的观点;似乎Hibernate应该能够在没有那么多努力的情况下处理这个问题。

以下是application.properties的配置设置:

spring.datasource.url=jdbc:mysql://localhost/sales?verifyServerCertificate=false&useSSL=true
spring.datasource.username=user
spring.datasource.password=pass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.show-sql=true
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

更改网址中架构的名称允许我访问该架构中的数据,但我无法访问除网址中列出的架构之外的任何其他架构。

6 个答案:

答案 0 :(得分:3)

我缺少的关键是配置和数据库类型。在MySQL中,数据库和模式之间没有真正的区别。使用@Table注释中的schema属性,它引用了一个" real" schema,但不是在MySQL中定义的。这解释了为什么schema属性适用于H2数据库而不适用于MySQL。

@Table(名称=" SCHEMA_NAME.TABLE_NAME")注释最初无效,因为我的数据源网址中有默认架构。网址需要

spring.datasource.url=jdbc:mysql://localhost?verifyServerCertificate=false&useSSL=true

有了这个改变,一切都适用于MySQL。

另外请注意,为了使其与H2一起使用,请确保定义hibernate.default_schema属性。

答案 1 :(得分:2)

处理此问题的方法是为名称冲突的实体指定 架构

使用

@Table(name="TABLE_NAME", schema="SCHEMA_NAME")

@Table(name="SCHEMA_NAME.TABLE_NAME")

答案 2 :(得分:2)

我刚刚将应用程序从使用手工制作的EntityManager配置迁移到了Spring Boot 2.0,然后立即出现了这些问题。

正确的做事方式就是使用

@Table(name="proposalstatuses",catalog = "sales")

...如@ suneet-khurana所建议。请注意,MySQL不支持架构,您需要使用catalog

但是,在我们的情况下,这需要更改许多实体,并且在短期内,我想避免代码更改,并使用配置恢复到将模式名嵌入表名的旧行为。

因此,经过一些研究和调试,我发现显式设置以下属性可以解决问题:

spring.jpa.properties.hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Spring Boot JPA中的默认实现似乎是SpringPhysicalNamingStrategy,通过用下划线替换所有点来消除名称的麻烦。

答案 3 :(得分:0)

在以下配置中无需删除默认数据库名称。

spring.datasource.url = jdbc:mysql:// localhost:3306 / defaultDb ? >

我发现了两个选项,可以使用Spring JPA在mysql查询中动态添加数据库名称。

  1. 如果要在@Table批注中添加目录选项,则目录选项将仅考虑数据库名称,并且数据库名称将像“ sales.proposalstatuses”一样附加在查询中。

    @Entity
    @Table(name="proposalstatuses",schema="sales",catalog = "sales")
    public class ProposalStatus implements Serializable {
    
  2. 在@Table批注中使用在“名称”选项中的tableName附加databaseName,如下所示。

    @Entity
    @Table(name="sales.proposalstatuses")
    public class ProposalStatus implements Serializable {
    

我已经编辑了https://stackoverflow.com/users/1100847/tim共享的上述示例,并且上述示例适用于spring boot(2.0.0.RELEASE),Sping JPA和mysql数据库(6.0.6)

答案 4 :(得分:0)

在Spring Boot项目中处理休眠envers时,我偶然发现了同样的问题。我的审核表保存在单独的架构中,但是由于我想将用户信息添加到修订信息中,因此我必须创建一个修订实体,默认情况下,它将转到默认架构,而不是审核架构。由于在此项目中,开发服务器为MySQL,而测试服务器和生产服务器必须为SQL Server,因此不能选择在catalog中指定@Table,因为catalog对于SQL Server具有不同的含义(和according to this rejected bug report这是预期的行为。

谢尔盖·伊万诺夫(Sergei Ivanov)的方法给我带来了问题,因为他们使用的NamingStrategy采用了不同的CamelCase转换方法,并且我不想为每个实体添加明确的@Table(name="...")注释。最后,我通过创建一个自定义NamingStrategy来扩展其方法,该自定义public class TableDotSpringPhysicalNamingStrategy extends SpringPhysicalNamingStrategy { public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) { return this.apply(name, jdbcEnvironment); } private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) { if (name == null) { return null; } else { StringBuilder builder = new StringBuilder(name.getText()); for(int i = 1; i < builder.length() - 1; ++i) { if (this.isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) { builder.insert(i++, '_'); } } return this.getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment); } } private boolean isUnderscoreRequired(char before, char current, char after) { return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after); } } 扩展了默认名称,并且仅覆盖表名转换,因此点不被下划线代替:

application.properties

然后在我的spring.jpa.properties.hibernate.physical_naming_strategy=my.package.path.TableDotSpringPhysicalNamingStrategy 中添加相关配置:

var i;
var modal = [];
var btn = [];
var span = [];

function open(i) {
    return function() {
      modal[i].style.display = "block";
    }
}
function closex(i) {
    return function() {
      modal[i].style.display = "none";
    }
}
function close(i) {
    return function(event) {
      if (event.target == modal[i]) {
        modal[i].style.display = "none";
      }
    }
}

for(i=1;i<3;i++)
{
    modal[i]= document.getElementById('challenge'+i+'Modal');
    btn[i] =document.getElementById("challenge"+i);
    span[i] = document.getElementById('challenge'+i+'Close');

    // When the user clicks the button, open the modal 
    btn[i].onclick = open(i);

    // When the user clicks on <span> (x), close the modal
    span[i].onclick = closex(i);
    // When the user clicks anywhere outside of the modal, close it
    window.onclick = close(i);
}

这样,在不取消默认模式的情况下指定其他模式就可以独立于数据库是否为MySQL。

答案 5 :(得分:0)

感谢您的所有回复,我想在调查结果中再加一点

  1. 我们在@Column批注的属性中提供的名称 应该与我们在@Table和@SecondaryTable中给出的名称匹配 注解。
  2. 因此,当我们在@Table中添加模式名称时,请 确保我们在@Column
  3. 中执行此操作

@Table(name="schema1.table")
@SecondaryTables({
    @SecondaryTable(name = "schema2.table")
})

@Column( name = "col1", table = "schema2.table")

注意:您不必为主表列指定表属性