Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving Spring Boot 1.3 to 1.4, Hibernate 4 to 5, Pascal Case Issues

I created a Spring Boot 1.3.5 POC with Spring Data JPA and Hibernate (4.3.11.Final in this version of Spring Boot). My backend database is Microsoft SQL Server, and our standard naming convention for database objects is pascal case (e.g. MySchema.MyTable.MyColumn). I used the javax.persistence.Table and javax.persistence.Column annotations to set the names, and added spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy to my application.properties file.

Everything worked perfectly. I even updated to Spring Boot 1.3.6 with no issues.

Now I moved to Spring Boot 1.4.0.RELEASE which uses Hibernate 5.0.9.Final, and the spring.jpa.hibernate.naming-strategy property is deprecated in favor of spring.jpa.hibernate.naming.strategy. I changed that property name, but left the EJB3NamingStrategy value. I also changed the other deprecated elements:

  • org.springframework.boot.orm.jpa.EntityScan to org.springframework.boot.autoconfigure.domain.EntityScan
  • org.springframework.boot.context.web.SpringBootServletInitializer to org.springframework.boot.web.support.SpringBootServletInitializer
  • org.springframework.boot.test.SpringApplicationConfiguration to org.springframework.boot.test.context.SpringBootTest (in my test classes)

Now the generated SQL uses the default camel case to underscore naming convention and not the pascal case that I had with EJB3NamingStrategy.

//application.properties
spring.data.jpa.repositories.enabled=true
spring.data.solr.repositories.enabled=false
spring.data.mongodb.repositories.enabled=false
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.EJB3NamingStrategy
#spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy

//hibernate.properties
hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
hibernate.format_sql=true

//Principal.java
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Size;

import org.hibernate.envers.AuditTable;
import org.hibernate.envers.Audited;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Entity
@Table(name="Principal", schema="Security")
@Audited
@AuditTable(value = "Principal", schema = "Audit")
public class Principal {

    private static final Logger LOG = LoggerFactory.getLogger(Principal.class);

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

    @Column(name = "Username", 
            nullable = false, 
            unique = true)
    @Size(min = 1, max = 64)
    private String name;

    @Column(name = "FirstName", 
            nullable = false)
    @Size(min = 1, max = 64)
    private String firstName;

    @Column(name = "LastName", 
            nullable = false)
    @Size(min = 1, max = 128)
    private String lastName;

    @Column(name = "IsEnabled", 
            nullable = false)
    private boolean enabled;

    //getters/setters omitted for brevity
}

orignal console output:

Hibernate: 
    select
        principal0_.Id as Id1_8_,
        principal0_.IsEnabled as IsEnable2_8_,
        principal0_.FirstName as FirstNam3_8_,
        principal0_.LastName as LastName4_8_,
        principal0_.Username as Username5_8_ 
    from
        Security.Principal principal0_ 
    where
        principal0_.Username=?

new console output:

Hibernate: 
    select
        principal0_.id as id1_7_,
        principal0_.is_enabled as is_enabl2_7_,
        principal0_.first_name as first_na3_7_,
        principal0_.last_name as last_nam4_7_,
        principal0_.username as username5_7_ 
    from
        security.principal principal0_ 
    where
        principal0_.username=?
2016-08-05 09:19:22.751  WARN 5032 --- [  XNIO-2 task-8] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 207, SQLState: S0001
2016-08-05 09:19:22.751 ERROR 5032 --- [  XNIO-2 task-8] o.h.engine.jdbc.spi.SqlExceptionHelper   : Invalid column name 'is_enabled'.
2016-08-05 09:19:22.768 ERROR 5032 --- [  XNIO-2 task-8] io.undertow.request                      : UT005023: Exception handling request to /springbootsecurity/login

I've searched extensively, and found references to ImplicitNamingStrategy and PhysicalNamingStrategy; but plugging those in don't seem to work and is probably not the correct approach. I've also seen references to creating my own NamingStrategy. Is that the route I must take?

Is there a different setting for Hibernate 5 that will use the exact name I provide in the @Table and @Column annotations? Is there a problem with the way I am defining the annotations?

like image 224
Rob Streeter Avatar asked Aug 05 '16 16:08

Rob Streeter


People also ask

Can we use spring boot with Hibernate?

To test hibernate configuration with Spring boot, we need to autowire the EmployeeRepository dependency in a class and use it's method to save or fetch employee entities. Let's do this testing in @SpringBootApplication annotated class and using CommandLineRunner interface.

Does spring boot use Hibernate by default?

Spring Boot configures Hibernate as the default JPA provider, so it's no longer necessary to define the entityManagerFactory bean unless we want to customize it. Spring Boot can also auto-configure the dataSource bean, depending on the database we're using.

How does Hibernate work in spring boot?

Hibernate understands the mappings that we add between objects and tables. It ensures that data is stored/retrieved from the database based on the mappings. Hibernate also provides additional features on top of JPA. But depending on them would mean a lock in to Hibernate.


1 Answers

I would like to say I ended up posting a silly question, but every direction I went talked about creating a custom naming strategy. The answer in my case, however, was simply using Hibernate's PhysicalNamingStrategyStandardImpl.

Added to application.properties:

spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

From my naive analysis, I'm assuming this works because I am using the @Table and @Column annotations. The PhysicalNamingStrategyStandardImpl appears to simply use the name in those annotations as the database object name.

So my Hibernate generated query is once again formatted as:

Hibernate: 
    select
        principal0_.Id as Id1_7_,
        principal0_.IsEnabled as IsEnable2_7_,
        principal0_.FirstName as FirstNam3_7_,
        principal0_.LastName as LastName4_7_,
        principal0_.Username as Username5_7_ 
    from
        Security.Principal principal0_ 
    where
        principal0_.Username=?

Reading @AmanTuladhar's link and this link from that post is where it eventually clicked for me. Thanks!

like image 158
Rob Streeter Avatar answered Oct 05 '22 22:10

Rob Streeter