Spring Boot 2 migration causes index problem on H2 with HikariCP - spring

After migrating Spring Boot in our application from 1.5.13.RELEASE to 2.1.8.RELEASE we noticed that our tests throws errors about index creation, but still passing,
In our production we are using PostgresSQL and in our tests we are using H2 DB version: 1.4.200
After exploring the bug, we noticed the following behaviour:
Lets assume that this our entities inside the application:
#Entity
#Table(indexes = #Index(columnList = "name", name = "name"))
public class Cat {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
private String name;
}
#Entity
#Table(indexes = #Index(columnList = "name", name = "name"))
public class Dog {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
private String name;
}
Then if we are running the tests with h2 there is exception:
Caused by: org.h2.jdbc.JdbcSQLException: Index "NAME" already exists; SQL statement:
create index name on dog (name) [42111-196]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.196.jar:1.4.196]
at org.h2.message.DbException.get(DbException.java:179) ~[h2-1.4.196.jar:1.4.196]
at org.h2.message.DbException.get(DbException.java:155) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.ddl.CreateIndex.update(CreateIndex.java:76) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.CommandContainer.update(CommandContainer.java:101) ~[h2-1.4.196.jar:1.4.196]
at org.h2.command.Command.executeUpdate(Command.java:260) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:192) ~[h2-1.4.196.jar:1.4.196]
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:164) ~[h2-1.4.196.jar:1.4.196]
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95) ~[HikariCP-3.2.0.jar:na]
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-3.2.0.jar:na]
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:54) ~[hibernate-core-5.3.11.Final.jar:5.3.11.Final]
... 34 common frames omitted
This are our application.properties:
server.port=9090
########## DATABASE CONFIGURATION ##############
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Even that Index in the database should create per Table and not on the schema, so the index should not exist already,
if we are running the same entities with Postgres then no error is been throwing,
I'm wondering if this is a bug in the H2 driver? or that we are doing something wrong?
After exploring a bit more I found this question from 2010,
It's says that the Index is global and has to be unique name, But this don't make any sense because as I mentioned before the index is per table, and not schema.
And here is a proof that in the DB index created per table
But if I'll create same index name for both tables, then only in the first table the index will be created.

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"")

SpringBoot JPA Bulk insertion is not woking - Master/Slave Database

Entity:
public class CapacityConfig {
#Id
#GeneratedValue(generator = "abc")
#GenericGenerator(name = "abc", strategy = "increment")
#Column(name = "id")
private Long id;
#Column(name = "sap_id")
private Long SapId;
}
Properties File:
spring.jpa.properties.hibernate.generate_statistics=true
spring.jpa.properties.hibernate.jdbc.batch_size=100000
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
#Master config
spring.oms.master.datasource.jdbcUrl=jdbc:mysql://****?reWriteBatchedInserts=true&useUnicode=yes&characterEncoding=UTF-8
spring.oms.master.datasource.username=**
spring.oms.master.datasource.password=**
spring.oms.master.datasource.maxconnection=2
spring.oms.master.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.oms.master.datasource.hikari.maxLifeTime=600000
#Slave config
spring.oms.master.datasource.jdbcUrl=jdbc:mysql://****?reWriteBatchedInserts=true&useUnicode=yes&characterEncoding=UTF-8
spring.oms.master.datasource.username=**
spring.oms.master.datasource.password=**
spring.oms.master.datasource.maxconnection=2
spring.oms.master.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.oms.master.datasource.hikari.maxLifeTime=600000
I am saving data like:
`CapacityConfigDAO.saveAllAndFlush(CapacityConfigList);
While saving data batch operations are not performing. Idk why?
Mat be there is some issue with configuration because I am using master/slave architecture.

Spring JPA - how to set data.sql to add data if not exist, ignore if exists

Hi i am creaeting rest api spring boot jpa & mysql. i want this to be automated as possible so i drop the database and schema and want spring to create all for me. i am adding data into mysql using data.sql. When i start spring boot, i 1 2 it to add into the table (which spring jpa help to create).
Tis is from my application.properties
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:mysql://localhost:3306/mockashop
spring.datasource.username=root
spring.datasource.password=root
spring.data.jpa.repositories.bootstrap-mode=default
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format.sql=true
spring.jpa.hibernate.ddl-auto=update
spring.sql.init.mode=always
here is my data.sql:
INSERT INTO `mockashop`.`user` (`user_id`, `user_role`, `user_contact`, `user_email`, `user_name`, `user_pswd`, `user_wallet`) VALUES ('20003', 'ROLE_USER', '+6592212152', 'shah3#gmail.com', 'shah3', 'shahshah', '777.55');
model:
#Entity
#Data
#SequenceGenerator(name="seq2", initialValue=2000, allocationSize=1)
public class User {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq2")
#Id
private int user_id;
#NotNull(message = "Name cannot be blank")
#Size(min=5, message = "Name must contain atleast 5 character")
private String user_name;
#NotNull(message = "Email cannot be blank")
#Email(message = "Enter valid email")
#Size(min=5, message = "Email must contain atleast 5 character")
private String user_email;
#Size(min=5, message = "Password must contain atleast 8 character")
#NotNull(message = "Password cannot be blank")
private String user_pswd;
#Size(min=5, message = "Contact must contain atleast 8 character")
#NotNull(message = "Password cannot be blank")
private String user_contact;
#NotNull(message = "Wallet cannot be blank")
private BigDecimal user_wallet;
private String USER_ROLE;
}
1ST PROB
spring hibernate will create the table for me if i create the schema first else it will give me error:
Caused by: java.sql.SQLSyntaxErrorException: Unknown database 'mockashop'
How do i resolve this?
2ND PROB
so i create the schema, then start spring, which give me this error:
Caused by: java.sql.SQLSyntaxErrorException: Table 'mockashop.user' doesn't exist
it will go away until i remove data.sql. now spring boot is finally running.
3RD PROB
mysql has the table created for me but no data. so i add back the data.sql. restart spring. now data is added.
4TH PROB
i do other changes and restart spring it gives me this error:
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '20001' for key 'user.PRIMARY'
i have to remove data.sql then the error is gone.
is there a way to resolve all this ?
1st
While h2 will create the schema for you automatically, MySQL won't automatically create the database for you. You have to do it manually.
Cant create a mysql database using Spring Boot
2nd and 3rd
If you are just adding just one line in mockashop.user, you can try adding the data using #EventListner
You can then remove the data.sql file
#Component
public class Data {
#Autowired
UserRepository userRepository;
#EventListener
public void appReady(ApplicationReadyEvent event){
rolesRepository.save(new User('20003', 'ROLE_USER', '+6592212152', 'shah3#gmail.com', 'shah3', 'shahshah', '777.55'));
}
}
4th
That is because every time you run this application the spring tries to add the same value over and over again and the user_id would have a UNIQUE constraint. That's why you are getting that error.
I believe the solution in the 2nd and 3rd point would also solve this problem as the save method of JPA would update the values in the database if the primary key value that is being passed is already present in the database.

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.

Spring, application.properties for creation of ElephantSQL tables

I try to do deploying (I use pivotal.io).
Before deploying I try to create tables of my DB.
On pivotal.io I create the test database (ElephantSQL). This new DB have:
Shared high performance cluster
20 MB data
4 concurrent connections
I use Spring and this describe my DB in application properties. This works if I create DB on my localhost.`
spring.datasource.url=jdbc:postgresql://stampy.db.elephantsql.com:5432/iyraxwqa
spring.datasource.username=iyraxwqa
spring.datasource.password=*************************
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.jpa.show-sql=false`
When I Run my application I see this ERROR:
2017-05-14 12:53:38.810 ERROR 4880 --- [ main] o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initial connections of pool.
org.postgresql.util.PSQLException: FATAL: too many connections for role "iyraxwqa"
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.readStartupMessages(QueryExecutorImpl.java:2586) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.<init>(QueryExecutorImpl.java:113) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:215) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.Driver.makeConnection(Driver.java:404) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.postgresql.Driver.connect(Driver.java:272) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:310) ~[tomcat-jdbc-8.5.6.jar:na]
at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) ~[tomcat-jdbc-8.5.6.jar:na]
at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:718) [tomcat-jdbc-8.5.6.jar:na]
I include hibernate h3p0 and add this code:
spring.jpa.properties.hibernate.c3p0.min_size = 1
spring.jpa.properties.hibernate.c3p0.max_size = 2
spring.jpa.properties.hibernate.c3p0.timeout = 300
But I see the same error.
If I try to create manually all is working, but I have a lot of tables and half year ago I created tables with spring and hibernate
One of my tables:
#Entity
#Table(name = "INTERIOR", schema = "public")
public class InteriorModel extends AllFinishProductModel {
#Column(name = "PHOTO")
private String photo;
#Column(name = "PHOTO01")
private String photo01;
#Column(name = "PHOTO02")
private String photo02;
#Id
#Column(name = "ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "COLOR")
private String color;
#Column(name = "QUANTITY")
private Double quantity;
#Column(name = "PRICE")
private BigDecimal price;
// getters and setters....
Somebody know, where my mistake?
I have the same problem, I think you are using the Free plan(Tiny Turtle). I think the problem is the max number of connection that PosgreSql(elephantsql server) support, to know the max limit connection you can execute the follow sql script in your ElephantSql browser:
select * from pg_roles where rolname='iyraxwqa'
it display your role configuration in postgresql and you can see the column 'rolconnlimit' to know the max number of connection supported
I don't understand why, but when I delete c3p0 and started to use tomcat everything works. This code is working:
spring.datasource.tomcat.max-wait=1000
spring.datasource.tomcat.max-active=3
spring.datasource.tomcat.test-on-borrow=true

Resources