Search code examples
javaspringhibernatejpahibernate-entitymanager

How to change Hibernate GenerationType identifier depending on the underlying database


I have a project called main_project (uses spring-boot v2) that contains all configuration classes including JPA configuration. The main_project also has entity classes (Like User, Personnel).

The JPA configuration for managed entity class in main_project is like below:

@Entity
public abstract class MainEntity<T extends Serializable> {
    @Id
    @GeneratedValue(GenerationType=?)
    @Column(name = "id")
    private T id;
}

@Entity
public class Personnel extends MainEntity<Long> {
    @Column(name = "firstName")
    private String firstName;

    // other proprties
}

Other projects are using main_project as dependency for bootstrapping. Other projects that depends on main_project can use Personnel or User and ... entities and they can have different DBMS's like MySQL or Oracle.

When i used main_project as a dependency in project A , entity class of A project extends MainEntity<?> and creates it's own entity classes and have its own database configuration file.

my problem is on type of DBMS and GenerationType on id property in main_project.

  • 1) when A project uses Mysql database, MainEntity must have:

    @GeneratedValue(strategy = GenerationType.IDENTITY)

  • 2) when A project uses Oracle database, MainEntity must have:

    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id_generator") @SequenceGenerator(name="id_generator", sequenceName = "id_seq", allocationSize=1)

How project A can detect its database type and switch between above approaches?

I think that i need reflected java util that in runtime and add some annotations based on databases type! is it right?

I also have read, this_post_19875993 but it didn't helped.

this_post_30731627 explained that we can select one of custom GenerationType but i want to select automatically without any changes to main_project, because A project can not change MainEntity class of main_project and just can used it.


Solution

  • You could use the SEQUENCE identifier in your base class:

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

    And override it for MySQL using the external mysql-orm.xml configuration file:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <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.vladmihalcea.book.hpjp.hibernate.identifier.global</package>
        <entity class="Post" access="FIELD">
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
            </attributes>
        </entity>
    </entity-mappings>
    

    So while for Oracle and PostgreSQL, you won't have to provide the mysql-orm.xml, for MySQL, just supply this file via the persistence.xml onfiguration file when you build the project using the following mysql profile:

    <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.vladmihalcea.book.hpjp.hibernate.identifier.global.Post
            </class>
     
        </persistence-unit>
    </persistence>
    

    That's it.