Spring Boot Data Initialization - spring

I am trying to insert some test data after my Spring Boot application has started. I have the below configuration in place. I do notice that the sql script is executed before the JPA entities are created resulting in a failure of the insert statement. Is there any other configuration I am missing here? As noted in my configuration I am using Spring Boot with H2 and EclipseLink for JPA.
Reference
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
Configuration
# H2
spring.h2.console.enabled=true
# Datasource
spring.datasource.url=jdbc:h2:mem:testdb;MODE=ORACLE
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
# JPA
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.properties.eclipselink.weaving=false
data.sql is placed under /src/main/resources
Logs
2017-09-05 20:37:37,989 [restartedMain ] INFO
o.s.j.d.e.EmbeddedDatabaseFactory - Starting embedded database:
url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false',
username='sa'
2017-09-05 20:37:38,141 [restartedMain ] INFO
j.LocalContainerEntityManagerFactoryBean - Building JPA container
EntityManagerFactory for persistence unit 'default'
2017-09-05 20:37:38,446 [restartedMain ] INFO
o.s.o.j.AbstractEntityManagerFactoryBean - Initialized JPA
EntityManagerFactory for persistence unit 'default'
2017-09-05 20:37:38,510 [restartedMain ] INFO o.s.j.d.i.ScriptUtils
- Executing SQL script from URL [file:/develop/src/data-init/target/classes/data.sql]
error...
Entity
#Entity
#Table(name = "TEMPLATE_CONFIG")
#Data
public class TemplateUser {
#Id
#Column(name = "TEMPLATE_CONFIG_ID", nullable = false)
private Long configId;
#Column(name = "APP_CODE", nullable = false, length = 100)
private String appCode;
...
}
Update
Uploaded a sample project:
https://git.io/v5SWx

Spring-Boot loads data.sql or data-${somthing}.sql
You can do:
spring.datasource.platform=h2.
spring.datasource.data=myscript-test.sql. ( locations can be changed with this property)
I have a project on that way and is working with h2.
And just how last alternative, if you are not able to load the information maybe you can use the CommandLineRunner in your application file with jdbc template.

This is an existing bug in Spring as confirmed here by the Spring Team.
https://github.com/spring-projects/spring-boot/issues/10365

Related

Bootstrapping Spring Data Neo4j repositories

I am starting with Spring Data Neo4j, using Spring Boot 3.0.0.
Created application with Spring Initializr, added a Node and a Repository.
Neo4jDemo,java
#SpringBootApplication
public class Neo4jDemo {
public static void main(String[] args) {
SpringApplication.run(Neo4jDemo.class, args);
}
}
Player.java
#Node("Player")
#RequiredArgsConstructor
public class Player {
#Id
private final String name;
}
PlayerRepository.java
public interface PlayerRepository extends ReactiveNeo4jRepository<Player, String> {
}
When I run this, It shows Finished Spring Data repository scanning message twice, with different repository count.
[ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Neo4j repositories in DEFAULT mode.
[ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 123 ms. Found 1 Neo4j repository interfaces.
[ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Neo4j repositories in DEFAULT mode.
[ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 2 ms. Found 0 Neo4j repository interfaces.
Am I missing something ?
Or just ignore these messages ?
Thanks
After adding following to application.properties, it shows Finished Spring Data repository scanning message, only once (with correct repository count)
application.properties
.
.
.
spring.data.neo4j.repositories.type = reactive
I guess, earlier it was looking for both (reactive and imperative) type of repositories.

Spring Boot 3 with Kotlin throws java.lang.UnsupportedOperationException for data class on GraalVM

Trying to run a very simple Spring Boot 3 app on GraalVM. The app just stores some data in H2 on startup. It is basically the same as what Josh does in the Spring Tips about Ahead-of-Time compilation, but using Kotlin instead of Java.
When starting the native image, I get the following error after Spring startup:
java.lang.UnsupportedOperationException: Kotlin class com.example.demo.basics.Customer has no .copy(…) method for property id
at org.springframework.data.mapping.model.BeanWrapper$KotlinCopyUtil.setProperty(BeanWrapper.java:171) ~[na:na]
at org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:79) ~[na:na]
...
I'm pretty new to GraalVM and Spring Native Images, don't know if I am missing something basic which needs to be configured when using Kotlin with Spring. The missing copy methods for the data class should have been generated by Kotlin, so I guess something is left out by the native compile related to Kotlin specifically.
The code which fails is the following class:
#Configuration
class BasicsConfiguration {
#Bean // execute on application start
fun basicsApplicationListener(customerRepository: CustomerRepository): ApplicationListener<ApplicationReadyEvent> {
return ApplicationListener<ApplicationReadyEvent> {
// store some values in the database
customerRepository
.saveAll(listOf("A", "B", "C").map { Customer(null, it) })
.forEach { println(it) }
}
}
}
interface CustomerRepository : CrudRepository<Customer, Int>
data class Customer(#Id val id: Long?, val name: String)
Running the app on the JDK works perfectly fine: ./gradlew bootRun
2022-11-30T11:23:15.300+01:00 INFO 33997 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 2.383 seconds (process running for 2.733)
Customer(id=1, name=A)
Customer(id=2, name=B)
Customer(id=3, name=C)
The native image is also created successfully: ./gradlew nativeCompile
Starting the native image works, the server process starts up but then fails:
2022-11-30T11:08:11.085+01:00 INFO 33059 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 0.147 seconds (process running for 0.158)
2022-11-30T11:08:11.089+01:00 ERROR 33059 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.UnsupportedOperationException: Kotlin class com.example.demo.basics.Customer has no .copy(…) method for property id
at org.springframework.data.mapping.model.BeanWrapper$KotlinCopyUtil.setProperty(BeanWrapper.java:171) ~[na:na]
at org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:79) ~[na:na]
...
Seems like this is an issue with Spring Data, which is not providing all necessary reflection hints for the native image creation. Specifically the reflection configuration for data classes' copy$default methods are missing. Will (probably) be fixed with Spring 6.0.3 release, see this issue for details: https://github.com/spring-projects/spring-framework/issues/29593
Workaround: add something like this in your reflect-config.json for the given data class:
{
"name": "copy$default",
"parameterTypes": [
"com.example.demo.basics.Customer",
"java.lang.Long",
"java.lang.String",
"int",
"java.lang.Object"
]
}

Spring Boot Data JPA with H2 and data.sql - Table not Found

I have a Spring Boot 2.5.0 project. I'm using spring-data-jap with the h2 in-memory database. I want to populate data on startup with a data.sql file but I'm getting a table not found exception. If I remove the data.sql file, I can see that a table for my entity does get created automatically. But if I include the data.sql file, I get the error saying the table doesn't exist. Maybe it is an error with my sql syntax of I have misconfigured the h2 database?
applicaltion.yml
spring:
datasource:
url: jdbc:h2:mem:test
driverClassName: org.h2.Driver
username: sa
password: sa
jpa:
database-platform: org.hibernate.dialect.H2Dialect
debug: true
data.sql
INSERT INTO BUSINESS_SUMMARY VALUES (1, "ALM470", "B48", 3);
BusinessSummary.java entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Getter
#Entity
public class BusinessSummary {
#Id
private Long id;
private String businessId;
private String businessDomainId;
private Integer cityCode;
}
BusinessSummaryRepository.java
#Repository
public interface BusinessSummaryRepository extends JpaRepository<BusinessSummary, Long> {
}
Exception:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "BUSINESS_SUMMARY" not found; SQL statement:
INSERT INTO BUSINESS_SUMMARY VALUES(1, "ALM470", "B48", 3) [42102-200]
spring.jpa.defer-datasource-initialization=true
By default, data.sql scripts are now run before Hibernate is
initialized. This aligns the behavior of basic script-based
initialization with that of Flyway and Liquibase.
If you want to use
data.sql to populate a schema created by Hibernate, set
spring.jpa.defer-datasource-initialization to true. While mixing
database initialization technologies is not recommended, this will
also allow you to use a schema.sql script to build upon a
Hibernate-created schema before it’s populated via data.sql.
you'll have to convert spring.jpa.defer-datasource-initialization to yml.
If you're using hibernate as a JPA implementation, the best way I think is by using the file import.sql instead of data.sql for Database Initialization.
for more information on database initialization see the official Spring Boot documentation Database Initialization
in addition to defer-datasource-initialization: true, you may also need
spring:
sql:
init:
mode: always
spring.jpa.defer-datasource-initialization = true
spring.sql.init.mode = always
if still doesn`t work try renaming the file from data.sql to import.sql

unexpected Spring Boot metrics for org.apache.tomcat.jdbc.pool.DataSource

I'm using Spring Boot with a pooled datasorce
datasource:
type: org.apache.tomcat.jdbc.pool.DataSource
driverClassName: com.mysql.jdbc.Driver
url: ...
username: ...
password: ...
tomcat:
max-active: 50
max-idle: 50
testOnBorrow: true
validationQuery: select 1;
validationInterval: 30000
This configuration is properly taken as the logfile contains 10x the following line:
16:27:52.191 [] [ restartedMain] DEBUG g.apache.tomcat.jdbc.pool.PooledConnection - Instantiating driver using class: com.mysql.jdbc.Driver [url=...]
After that, I start using the application and made some database requests. The DAO implementation is using JPAContext und EntityManager, autowired by Spring and works perfectly returning the expected results from the database.
#Autowired
private JpaContext jpaContext;
#Autowired
private EntityManager em;
EntityManager em = jpaContext.getEntityManagerByManagedType(DownloadHistoryItemCustomEntity.class);
Query q = em.createNativeQuery(query, DownloadHistoryItemCustomEntity.class);
However, the Spring Boot metrics doesn't show any usage for that single datasource
http://localhost:8080/metrics
"datasource.primary.active": 0,
"datasource.primary.usage": 0.0
Why there aren't values > 0 ?
I would expect values greater then zero !
Isn't 'primary' the right datasource ?
Dominik
datasource.primary.active is the current number of active connections. datasource.primary.usage is a value between 0 and 1 where 0 indicates that there are no active connections and 1 indicates that every connection in the pool is active. For the values to be non-zero, a database connection will have to be active when you make the request to the /metrics endpoint.

Using Spring Cloud Connectors together with HikariCP

I would like to use HikariCP from the Spring Cloud Connectors. I am not sure how to proceed...
I have updated my Spring Cloud Connectors to 1.2.0.RC1.
Here is my current config:
#Configuration
#Profile({ Profiles.CLOUD })
public class CloudDataSourceConfiguration extends AbstractCloudConfig {
#Bean
public DataSource dataSource() {
int dbcpMaxActive = 10;
int dbcpMaxWait = 200;
PoolConfig poolConfig = new PoolConfig(dbcpMaxActive, dbcpMaxWait);
ConnectionConfig connectionConfig = new ConnectionConfig("sessionVariables=sql_mode='ANSI';characterEncoding=UTF-8");
DataSourceConfig serviceConfig = new DataSourceConfig(poolConfig, connectionConfig);
return connectionFactory().dataSource("CLEARDB_DATABASE", serviceConfig);
}
}
Can someone please advise?
edit: When I start the app with the cloud profile, I can read
2015-05-23 22:46:56,029 [localhost-startStop-1] INFO org.springframework.cloud.service.relational.PooledDataSourceCreator - Found Tomcat high-performance connection pool on the classpath. Using it for DataSource connection pooling.
from the log output.
edit 2: HikariCP is in the classpath and it seems that tomcat high performance connection pool is also in the classpath.
As stated in my second edit, both tomcat jdbc & HikariCP were on the classpath. By removing tomcat jdbc as follows (in my gradle script):
compile("org.springframework.boot:spring-boot-starter-data-jpa"){
exclude group: 'org.apache.tomcat', module: 'tomcat-jdbc'
}
only HikariCP remained on the classpath and it was picked up properly as shown by the log output below:
INFO org.springframework.cloud.service.relational.PooledDataSourceCreator - Found HikariCP on the classpath. Using it for DataSource connection pooling.

Resources