简体   繁体   中英

Hibernate @Formula doesn't include Schema

I have an entity with a property @Formula like this:

@Entity
@Table(name = "areasAuxiliar")
public final class AreaAuxiliar implements Serializable {

    @Id
    @Column(name = "idArea")
    private Integer idArea;

    @Formula("RUTAAREA(idArea)")
    private String ruta;

when I configure my hibernate to point to an Oracle DB I have no problem, BUT, when I switch to an SQLServer, hibernate is not including the shema and the query fails,

the query generated for hibernate looks like this:

select
    areaauxili4_.idArea as idArea1_6_4_,
    rutaArea(areaauxili4_.idArea) as formula2_4_
from
    SIGAP.areasAuxiliar areaauxili4_ 

the param hibernate.default_schema=SIGAP is being read and included in the table but not in the function,

is there an option/annotation to force the shema in in that function?

I have tried hibernate 5.1 and 5.2 with the same result :(

You can use a mysql-orm.xml file to override your formula then configure your build to tale into account the file when the database is mysql.

Overriding here the formula :

<entity-mappings
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm orm_2_1.xsd"
    version="2.1">
    <package>com.acme.persistence</package>
    <entity class="AreaAuxiliar" access="FIELD">
        <attributes>
            <property name="ruta" formula="schemaName.RUTAAREA(idarea)"/>
        </attributes>
    </entity>
</entity-mappings>

Then add the reference in a specific persistence.xml . You then override your default persistence.xml with this one in your build or at runtime (cf. links below).

<persistence
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">

<persistence-unit name="persistenceUnit">

    <provider>
        org.hibernate.jpa.HibernatePersistenceProvider
    </provider>

    <mapping-file>
        mappings/identifier/global/mysql-orm.xml
    </mapping-file>

    <class>
        com.acme.persistence.AreaAuxiliar 
    </class>

</persistence-unit>

Note : heavlily inspired from How to change Hibernate GenerationType identifier depending on the underlying database

Note (2) : In the blog post and here , the author generate the PersistenceUnitInfo at run-time.

Not sure if this helps apply to the function, but have you tried adding the property 'schema' to the @Table annotation:

@Entity
@Table(name = "areasAuxiliar", schema="mySchemaName")
public final class AreaAuxiliar implements Serializable {

    @Id
    @Column(name = "idArea")
    private Integer idArea;

    @Formula("RUTAAREA(idArea)")
    private String ruta;

Another solution, which was converted to a comment, is to use a place holder in the @Formula annotation

@Entity
@Table(name = "areasAuxiliar")
public final class AreaAuxiliar implements Serializable {

    @Id
    @Column(name = "idArea")
    private Integer idArea;

    @Formula("{SCHEMA_AND_FUNCTION}")
    private String ruta;

Then Add an Interceptor to populate the formula's value. The link to the solution is Hibernate @Formula set value at runtime

Lastly, please see my comment about creating a global function in SQLSERVER. The details can be found here Can I create create a global function in SQL Server?

1) I know that for native queries, you can use the "{h-schema}" placeholder (which will populated with the value of the "hibernate.default_schema" param):

"SELECT x FROM {h-schema}tableName"

Give a try and see if by chance this is also working in @Formula...

2) If not, you might also try to play with substitutions (ie hibernate.query.substitutions) in order to tell hibernate to replace literal S with S' - in your case "RUTAAREA" with "schema.RUTAAREA" ?

a more simplified solution:

change your @Formula from this:

@Formula("RUTAAREA(idArea)")

to this:

@Formula("{MYAPP_SCHEMA}.RUTAAREA(idArea)")

create a class:

public class HibernateEntityInterceptor extends EmptyInterceptor {

}

register it as an Entity Interceptor in your sessionFactory, in my case:

sessionFactory.setEntityInterceptor(new HibernateEntityInterceptor());

then in that class you override this method:

public String onPrepareStatement(String sql) {

that method recive the sql command before it is executed, so, all you need to do is a simple replace all:

sql = sql.replaceAll("\\{MYAPP_SCHEMA}", default_schema);
return sql;

thanx for the help.

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