Entity Class name is transformed into SQL table name with underscores - spring-boot

I have the following entity defined:
#Entity
#Table(name = "EmailTemplate")
public class EmailTemplate {
Despite the table annotation, I receive java.sql.SQLException: Invalid object name 'email_template'. How can I prevent an entity class such as EmailTemplate being transformed into email_template table name?
Edit:
I'm using Spring Boot: start JPA. From my build.gradle file,
compile("org.springframework.boot:spring-boot-starter-data-jpa")

Spring by default uses org.springframework.boot.orm.jpa.SpringNamingStrategy which splits camel case names with underscore. Try setting spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy in application.properties. Check out this and this for more info.

For hibernate v5:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

For Spring Boot 2 (checked with 2.2.6.RELEASE) it should be configuration yml file:
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
So you could have model like:
#Table(name = "tblDepartments")
public class Department {
#Id
#Column(name = "dpID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#Size(min = 1, max = 25)
#Column(name = "dpName", length = 25)
private String name;
and populate tables at startup with data.sql:
INSERT INTO tblDepartments (dpName) VALUES ('Gryffindor');
INSERT INTO tblDepartments (dpName) VALUES ('Hufflepuff');

Use this in your appplication.properties.
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl

Use
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultNamingStrategy

In application.properties set
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

There are two most common org.hibernate.boot.model.naming.PhysicalNamingStrategys:
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
# also deprecated in 2.6 in favor of CamelCaseToUnderscoresNamingStrategy
# for removal in 2.8
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties holds:
private void applyNamingStrategies(Map<String, Object> properties) {
applyNamingStrategy(properties, AvailableSettings.IMPLICIT_NAMING_STRATEGY, this.implicitStrategy,
() -> SpringImplicitNamingStrategy.class.getName());
applyNamingStrategy(properties, AvailableSettings.PHYSICAL_NAMING_STRATEGY, this.physicalStrategy,
() -> CamelCaseToUnderscoresNamingStrategy.class.getName());
}
so by default CamelCaseToUnderscoresNamingStrategy is in use and you have underscores...

org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a];
nested exception is org.hibernate.exception.SQLGrammarException: could not execute statement
Both are required :
implicit-strategy
physical-strategy

Solved.
Invalid Object Name: Springboot with JPA(SQL server)
In application.yaml/properties specify the
spring.jpa.hibernate.naming.implicit-strategy
spring.jpa.hibernate.naming.physical-strategy
jpa:
show-sql: false
hibernate:
ddl-auto: none # Defaults to "none" when NOT in embedded mode
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Related

hibernate not use #column(name='') annotation

I use springboot 2.6 and postgres 15 and hibernate 5.7
I use database Generate persistence Mapping to make entity from database schema;
When I run application with this config:
spring.jpa.hibernate.ddl-auto=validate
table properties name not work.
for example this is my field:
#Basic
#Column(name = "isEnabled", nullable = false)
private boolean isEnabled;
When I run applicataion get this error:
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [is_enabled] in table [analytics]
Can you try to add the following annotation to your Analytics entity definition?
#Table(name = "analytics", schema = "public")
there are 2 problems. first is field name. seccond is table name;
first problem fix with this config in application.properties:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
this fix first problem. but seccond problem not fix.
you should change table name with this trick:
#Table(name = ""analytics"")

Unknown data type: "DEFAULT"; SQL statement: while using hibernate.ddl-auto:true

I am using H2 inMemory database for my springboot application. Where I have enabled hibernate.ddl-auto. Below is the application.yml content:
spring:
profiles: mock
jpa:
database-platform: org.hibernate.dialect.H2Dialect
generate-ddl: true
hibernate:
ddl-auto: create
datasource:
jdbcUrl: jdbc:h2:mem:master
driver-class-name: org.h2.Driver
username:
password:
maximumPoolSize: 10
minimumIdle: 5
idleTimeout: 6000
maxLifetime: 12000
leakDetectionThreshold: 18000
poolName: "primary"
and my entity class has an enumerated attribute as:
#Column(name = "SOURCE", columnDefinition = "default 'MOBILE_APP'")
#Enumerated(EnumType.STRING)
private DocSource source;
spring throwing Unknown data type: DEFAULT exception while it's creating a schema for the above entity as its taking dataType as "Default" from columnDefinition = "default 'MOBILE_APP'"
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: Unknown data type: "DEFAULT"; SQL statement:
create table DMS_CUSTOMER_DOCUMENT_REF (ID bigint generated by default as identity, APPLICATION_ID varchar(255), CREATED_DATE timestamp, CUSTOMER_DOC_TYPE_ID bigint, DMS_DOC_ID varchar(255), DOC_VIEW default 'FRONT', IS_DELETED boolean, SOURCE default 'MOBILE_APP', primary key (ID)) [50004-200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:505)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
at org.h2.message.DbException.get(DbException.java:205)
at org.h2.message.DbException.get(DbException.java:181)
at org.h2.command.Parser.parseColumnWithType(Parser.java:5971)
at org.h2.command.Parser.parseColumnForTable(Parser.java:5697)
at org.h2.command.Parser.parseTableColumnDefinition(Parser.java:8442)
at org.h2.command.Parser.parseCreateTable(Parser.java:8379)
at org.h2.command.Parser.parseCreate(Parser.java:6276)
at org.h2.command.Parser.parsePrepared(Parser.java:903)
at org.h2.command.Parser.parse(Parser.java:843)
at org.h2.command.Parser.parse(Parser.java:815)
at org.h2.command.Parser.prepareCommand(Parser.java:738)
at org.h2.engine.Session.prepareLocal(Session.java:657)
at org.h2.engine.Session.prepareCommand(Session.java:595)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1235)
at org.h2.jdbc.JdbcStatement.executeUpdateInternal(JdbcStatement.java:164)
at org.h2.jdbc.JdbcStatement.executeUpdate(JdbcStatement.java:126)
at com.zaxxer.hikari.pool.ProxyStatement.executeUpdate(ProxyStatement.java:120)
at com.zaxxer.hikari.pool.HikariProxyStatement.executeUpdate(HikariProxyStatement.java)
Is there a way where H2 will convert these types of column definitions in a correct way?
Try to correct your field definition in this way:
#Column(name = "SOURCE", columnDefinition = "VARCHAR(20) default 'MOBILE_APP'")
#Enumerated(EnumType.STRING)
private DocSource source;
columnDefinition will override the sql DDL generated by hibernate for this particular column, but please note that it is non portable and depends on database you use.
So, as an alternative you can use #ColumnDefault annotation:
#Entity
#DynamicInsert
public class YourEntity
{
// ...
#Column(name = "SOURCE")
#ColumnDefault("'MOBILE_APP'")
#Enumerated(EnumType.STRING)
private DocSource source;
}
See also this section of hibernate documentation.

Import data at startup Spring boot

I'm trying to launch a SQL file at my database initialization.
Here is my configuration:
spring:
profiles: local
jpa:
properties:
hibernate.temp.use_jdbc_metadata_defaults: false
generate-ddl: true
hibernate:
ddl-auto: update
database: h2
show-sql: true
autoCommit: false
datasource:
platform: h2
url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;
driver-class-name: org.h2.Driver
initialization-mode: always
data: classpath:/sql/CreateGeographicZones.sql
My script is just this line (atm):
INSERT INTO GEOGRAPHIC_ZONE (name) VALUES ('EUROPE');
And the related entity:
#NoArgsConstructor
#Data
#Entity
#Table(name = "GEOGRAPHIC_ZONE")
public class GeographicZone {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "geo_zone_sequence")
#SequenceGenerator(name = "geo_zone_sequence", sequenceName = "geo_zone_id_seq", allocationSize = 1)
#Column(nullable = false)
private Long id;
...
}
The table is created as I can see in the logs:
Hibernate: create table geographic_zone (id bigint not null, name varchar(100) not null, primary key (id))
But I have an SQL error when the script is executed:
Table "GEOGRAPHIC_ZONE" not found; SQL statement:
INSERT INTO GEOGRAPHIC_ZONE (name) VALUES ('EUROPE')
In the logs I can see that my table is created before the script execution, so why it's not working ?
According with your entity's metadata Hibernate is querying geo_zone_id_seq sequence's next value and using it for the ID on each insert.
If you would like to use the same approach when inserting directly in your database then you will need to implement a H2 Trigger
Also you may use either the EntityManager bean or your Spring JPA Repository to insert your data after application startup via CommandLineRunner interface.
Using EntityManager:
#Bean
CommandLineRunner registerZonesDataRunner(EntityManager entityManager, TransactionTemplate transactionTemplate) {
return args -> transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// presuming that GeographicZone has a constructor expecting NAME
Stream.of("AFRICA", "EUROPE")
.map(GeographicZone::new)
.forEach(entityManager::persist);
}
});
Using Spring JPA Repository:
#Bean
CommandLineRunner registerZonesDataRunner(GeographicZoneRepository repository) {
// presuming that GeographicZone has a constructor expecting NAME
return args -> repository.saveAll(Stream.of("AFRICA", "EUROPE")
.map(GeographicZone::new)
.collector(Collectors.toList()));
}
minimal, reproducible example
You don't show how you've defined the id column but the schema indicates there is no auto-generation scheme. So, try:
INSERT INTO GEOGRAPHIC_ZONE (id, name) VALUES (1, 'EUROPE');
in your data file. If that works, you'll need to either manually set the id in your inserts or add something like #GeneratedValue(strategy = AUTO) to your #Id property.

Hibernate does not generate Sequence when it's already available on a different schema

I have a "simple" Spring Boot application with a single datasource.
The following configuration is present in my configuration:
spring:
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
default_schema: CORE
flyway:
schemas:
- CORE
and the following ID Generator
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "seq-pooled-lo")
#GenericGenerator(
name = "seq-pooled-lo",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.INCREMENT_PARAM,
value = "50"
),
#org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.OPT_PARAM,
value = "pooled"
),
#org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.SEQUENCE_PARAM,
value = "seq_pooled_lo_sequence"
)
})
Now my issue is the following:
When I launch this app, it will create a sequence on the "CORE" schema. Everything works fine. Storing, retrieving data is no problem. When I then launch a second instance of the app but override the YAML file to define a different default_schema: SECOND it will not generate a new sequence on the "SECOND" schema. If I would first start the app with the "SECOND" schema defined and then start the one with "CORE" it would create the sequence on the "SECOND" schema and not on "CORE".
I would expect it to create different sequences on both schemas. Why does it not do that?
I also tried to add the sequence manually to the schema where it's missing but sadly that did not seem to help.
If you add the sequence manually to the schema.
You can create CustomIdGenerator Class as the example for return sequence id by defaultSchema
public class CustomIdGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SessionImplementor sessionImplementor, Object o) throws HibernateException {
SequenceRepository sequenceRepository = ApplicationContextProvider.getApplicationContext().getBean(SequenceRepository.class);
Environment env = ApplicationContextProvider.getApplicationContext().getBean(Environment.class);
String defaultSchema = env.getProperty("yourproperty.default_schema");
return sequenceRepository.getSequence(defaultSchema);
}
}
Let's create SequenceRepository class for generate sequence. I assume that my database is Oracle.
NEXTVAL: Increments the sequence and returns the next value
public interface SequenceRepository extends JpaRepository<Object,Long> {
#Query(value = "SELECT ?1.NEXTVAL FROM DUAL", nativeQuery = true)
Long getSequence(String sequenceName);
}
and your Entity just edit strategy to using CustomGenerator Class
#Id
#GenericGenerator(name = "sequence_generator", strategy = "yourpackage.CustomIdGenerator")
#GeneratedValue(generator = "sequence_generator")
#Column(name = "id")
private Long id;

Sequence "HIBERNATE_SEQUENCE" not found; SQL statement

In my spring mvc app, i have the following object. I am trying to make a visual of data using devtool in my app.
#Entity
#Data
public class ConsultationRequest {
#Id
#GeneratedValue
private Long id;
private String name;
private String email;
private String purpose;
private String programme;
private int year;
private String language;
private String comments;
#Enumerated(EnumType.STRING)
private ConsultationStatus status;
}
Then i used the jpa to make the entity:
#Repository
public interface ConsultationRequestRepository extends JpaRepository<ConsultationRequest, Long> {
}
The problem is when i load my application, i face with 2 errors:
Unsuccessful: drop sequence hibernate_sequence
[36morg.hibernate.tool.hbm2ddl.SchemaExport Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:
Then when i open the
http://localhost:8080/h2-console/
I cannot see the table.
It seems that the in the boot process, table is not made.
Update your code as below:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
As you have not specified a sequence table name, hibernate will look for a sequence table named as hibernate_sequence and use it as default.
For Oracle/Postgres, increment fields used are sequence tables.
In MySql, there are increment fields that automatically increment.
If someone is getting this error with Spring Boot testing(with H2) , we have to use following in the application.properties(or whatever profile we are using) :
spring.jpa.hibernate.ddl-auto=create-drop
Setting the following property in the properties file helped me solve the hibernate_sequence problem for hibernate 5.4v
spring:
jpa:
hibernate:
use-new-id-generator-mappings: false
Check persistence.xml
property name="hibernate.hbm2ddl.auto" value="create"
not hdm2ddl
This worked in my case.
SQL operation ERROR when start app spring-boot.
I added the setting in spring properties and solved: in the spring:jpa.hibernate.ddl-auto= create-drop to hibernate be able create or drop table automatically.
If you use a 2nd cache with liquidbase, you have to add the sequence in the changelog like this:
<changeSet author="liquibase-docs"
id="createSequence-example">
<createSequence catalogName="cat" cycle="false"
incrementBy="1" ordered="true" schemaName="public"
sequenceName="hibernate_sequence" startValue="0" />
</changeSet>
For spring-boot 2.7.x and h2 2.x you need to add MODE=LEGACY; in the database connection:
example application.yml:
spring:
datasource:
url: jdbc:h2:mem:test;MODE=LEGACY;
exemple application.properties:
spring.datasource.url=jdbc:h2:mem:test;MODE=LEGACY;
For Mysql:
the auto-increment is not added, modify ur table:
ALTER TABLE table_name MODIFY COLUMN id BIGINT AUTO_INCREMENT=1

Resources