I am trying to initialize my database with a view and a rule following 75.3 Initialize a database using Spring JDBC.
75.3 Initialize a database using Spring JDBC
Spring JDBC has a DataSource initializer feature. Spring Boot enables it by default and loads SQL from the standard locations schema.sql and data.sql (in the root of the classpath). In addition Spring Boot will load the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform, e.g. you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql etc.). Spring Boot enables the fail-fast feature of the Spring JDBC initializer by default, so if the scripts cause exceptions the application will fail to start. The script locations can be changed by setting spring.datasource.schema and spring.datasource.data, and neither location will be processed if spring.datasource.initialize=false.
This section says that if I put a schema-postgresql.sql it should initialize my database with the script that the file contains.
Unfortunately the script ends with the following error
Caused by: org.postgresql.util.PSQLException: syntax error at end of input SQL state
Position: 169
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2310) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2023) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:217) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:421) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:310) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7]
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:473) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]
... 64 common frames omitted
However if I run this script from pgAdminIII there are no errors and the view with the corresponding rule are created without any problem.
What am I doing wrong here?
This is the structure of my Spring Boot example to reproduce it.
src/main/java/com/example/model/Person.java
package com.example.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1334414548362400146L;
#Id
private long id;
#Column(nullable = false, length = 100)
private String name = "";
#Column(nullable = false, length = 100)
private String surname = "";
}
src/main/java/com/example/model/PersonRole.java
package com.example.model;
import java.io.Serializable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
#Entity
public class PersonRole implements Serializable {
private static final long serialVersionUID = -3953147119216643027L;
#EmbeddedId
private PersonRoleKey primaryKey;
}
src/main/java/com/example/model/PersonRoleKey.java
package com.example.model;
import java.io.Serializable;
import javax.persistence.Embeddable;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ForeignKey;
import javax.persistence.ManyToOne;
import javax.persistence.PrimaryKeyJoinColumn;
#Embeddable
public class PersonRoleKey implements Serializable {
private static final long serialVersionUID = 2105526364632711640L;
#ManyToOne(optional = false)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "person_fk"))
private Person person;
#Enumerated(EnumType.STRING)
private Role role;
}
src/main/java/com/example/model/Role.java
package com.example.model;
public enum Role {
ADMIN, USER;
}
src/main/java/com/example/DemoApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
src/main/resources/application.properties
#Database configuration
spring.datasource.url: jdbc:postgresql://localhost:5432/postgres
spring.datasource.driverClassName: org.postgresql.Driver
spring.datasource.username: postgres
spring.datasource.password: postgres
spring.datasource.platform: postgresql
spring.datasource.continue-on-error: false
spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql: true
spring.jpa.generate-ddl: true
spring.jpa.hibernate.ddl-auto: update
#default means org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.properties.hibernate.implicit_naming_strategy: default
spring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#spring.jpa.properties.hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
spring.jpa.properties.hibernate.auto_quote_keyword: true
spring.jpa.show-sql: false
src/main/resources/schema-postgresql.sql
CREATE OR REPLACE VIEW v_peoples_roles AS
SELECT p.id,
p.name,
p.surname,
pr.role
FROM (person p
JOIN personrole pr ON ((p.id = pr.person_id)));
CREATE OR REPLACE RULE insert_v_peoples_roles AS
ON INSERT TO v_peoples_roles DO INSTEAD ( INSERT INTO person (id, name, surname)
VALUES (new.id, new.name, new.surname);
INSERT INTO personrole (person_id, role)
VALUES (new.id, new.role);
);
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<hibernate.version>5.2.2.Final</hibernate.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The whole code could be downloaded from here.
EDIT
Based on the poz's comment I changed the schema-postgresql.sql to
CREATE OR REPLACE VIEW v_peoples_roles AS
SELECT p.id,
p.name,
p.surname,
pr.role
FROM (person p
JOIN personrole pr ON ((p.id = pr.person_id)));
CREATE OR REPLACE FUNCTION insert_into_v_people_roles() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO person (id, name, surname) VALUES (new.id, new.name, new.surname);
INSERT INTO personrole (person_id, role) VALUES (new.id, new.role);
RETURN new;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS insert_v_peoples_roles ON v_peoples_roles;
CREATE TRIGGER insert_v_peoples_roles INSTEAD OF INSERT ON v_peoples_roles FOR EACH ROW EXECUTE PROCEDURE insert_into_v_people_roles();
But it produces another error, exactly like predicted.
ERROR: unterminated quoted string at or near "$$ BEGIN INSERT INTO person (id, name, surname) VALUES (new.id, new.name, new.surname)"
Because pozs has not posted his own answer and some time has passed I am doing it by myself.
Changing the CREATE RULE to an INSTEAD OF triggered and $$-quoting to '-quoting solved the problem. The only issue was that I had to escape all the apostrophes inside function definitions. Was not that big of a pain though.
Related
I should probably mention that Spring is my first framework that I am learning for Java.
What I am trying to do is to get the JDBC working for me using the Spring Data JDBC dependency I downloaded from spring.io. (2.6.4) I followed a tutorial on YouTube where the guy demonstrated it with something similar to the following code. (Should also mention the code he used was for Spring version 2.2 but I'm not sure where else to find right code for 2.6.4)
There is also an UnsatisfiedDependencyException I keep getting where it says:
Error creating bean with name 'applicationRunner' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'applicationRunner' parameter 0;
I do not know why this is happening. But here is the actual code:
package com.example.demo;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
ApplicationRunner applicationRunner(StudentRepo studentRepo) {
return args -> {
var s1 = Student.createStudent("John", "Doe");
var s2 = Student.createStudent("Jane", "Doe");
System.out.println(studentRepo.save(s1));
System.out.println(studentRepo.save(s2));
System.out.println(studentRepo.findByFName("John"));
};
}
}
CRUD Repository interface:
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface StudentRepo extends CrudRepository<Student, String> {
#Query("SELECT * FROM Students WHERE name = :name")
List<Student> findByFName(#Param("name") String fName);
}
schema.sql file:
CREATE TABLE Students (
id VARCHAR(50) IDENTITY PRIMARY KEY,
firstName VARCHAR(50),
lastName VARCHAR(50),
rank VARCHAR(50)
);
I think it's also important for me to include pom.xml file: (Though it should also be mentioned that the pom.xml file didn't originally include the hsqldb dependency. I added that in myself.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Edit:
Exception after including #Repository in the CRUD repository interface:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationRunner' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'applicationRunner' parameter 0
Exception after replacing #Repository with #NoRepositoryBean:
2022-03-23 18:30:06.531 INFO 98567 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-03-23 18:30:06.556 ERROR 98567 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Parameter 0 of method applicationRunner in com.example.demo.DemoApplication required a bean of type 'com.example.demo.StudentRepo' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.StudentRepo' in your configuration.
Process finished with exit code 1
try modifying the bean:
#Bean
public CommandLineRunner demo(StudentRepo studentRepo) {
return (args) -> {
studentRepo.save(new Student("Jack", "Bauer"));
studentRepo.save(new Student("Chloe", "O'Brian"));
}
you can consult the guide https://spring.io/guides/gs/accessing-data-jpa/
The problem lies in the schema.sql script. An IDENTITY column in HSQLDB has to be of the type INTEGER or BIGINT (see documentation at http://www.hsqldb.org/doc/1.8/guide/ch09.html, section CREATE TABLE). All other errors are just subsequent errors.
I have been checking all of my code for hours now and i just can't seem to find tthe error in here
it keeps throwing org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'imageRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Not a managed type: class entitiys.Image
my controller looks like this:
package imageSearcher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class AppController {
public static void main(String[] args) {
SpringApplication.run(AppController.class, args);
}
}
this is what the Image entity looks like:
package entitiys;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity(name = "image")
public class Image {
#Id
#Column(name = "id")
public Integer id;
#Column(name= "imageURL")
public String imageURL;
}
The image mapping looks as following
package imageSearcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import entitiys.Image;
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class ImageController {
#Autowired
private ImageRepository imageRep;
#GetMapping(path="/all")
public Iterable<Image> index() {
return imageRep.findAll();
}
#GetMapping(path = "/all/URL")
public Iterable<String> AllURL() {
return imageRep.findAllURL();
}
}
the imahe CrudRepository looks like this:
package imageSearcher;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import entitiys.Image;
#Repository
public interface ImageRepository extends CrudRepository<Image, Integer>{
#Query(value = "select * from Image", nativeQuery = true)
Iterable<Image> findAll();
#Query(value = "select Image.imageURL from Image", nativeQuery = true)
Iterable<String> findAllURL();
}
this is the pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Use MySQL Connector-J -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and this is the application.properties:
server.port=91
spring.datasource.url=jdbc:mysql://localhost:3306/gip2021
spring.datasource.username=root
spring.datasource.password=
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.naming_startegy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.hibernate.ddl-auto=none
I have no clue of what is wrong here i hope someone can help me because this is for my final project and even my Teacher can't see the issue
also here is the database script for if you need it:
create table Image(
id integer unsigned auto_increment primary key,
imageURL varchar(255)
);
insert into image (imageURL) values
("test"),
("test2");
for anyone wanting the git repo:
https://github.com/michiel2003/GIP2021.git
Just moved everything to the same package and it worked my teacher told me to put the entitiys in a different package and that's not what you are supposed to do
The from spring boot documentation for #SpringBootApplication :
Many Spring Boot developers always have their main class annotated
with #Configuration, #EnableAutoConfiguration and #ComponentScan.
Since these annotations are so frequently used together (especially if
you follow the best practices above), Spring Boot provides a
convenient #SpringBootApplication alternative.
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan with their
default attributes: [...]
and #ComponentScan:
If specific packages are not defined, scanning will occur from the
package of the class that declares this annotation.
So only imageSearcher package is being scanned. You need to rearrange you packages or change #SpringBootApplication annotation to
#SpringBootApplication(scanBasePackages = {"imageSearcher","entitiys"})
EDIT:
If your teacher told you to move it to different package maybe he wanted you to make workaround for just like I proposed
created the pull request: https://github.com/michiel2003/GIP2021/pull/1 that will fix the bug, you can simply merge it :)
agreeing with every points that have already been mentioned above regarding #ComponentScan and adding additional things that caught my eyes:
this bug can be easily reproduced by adding a dummy loadContext test, this test will be generated when you start bootstrapping a spring boot application by using spring initializr, which is always the best way to start spring booting
I know test experience has always been treated as an overhead for junior developers, but it is worth it if you want to go down the road to a senior :)
when looking into your git histories: please do not simply delete getter and setters just because they look like template code, if you feel comfortable you can try adding lombok
I searched for similar topics. There are plenty of situations with this kind of problem but none was exactly mine, and none provided help. I am completly stuck at the moment, so I would really appreciate your help. Here's the situation:
I am trying to build a very simple app and, for now, just trying to connect everything together. My config files are the following:
pom.xml (containing a profile to facilitate the deployment):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hello</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<dependency>
<groupId>fish.payara.extras</groupId>
<artifactId>payara-embedded-all</artifactId>
<version>5.182</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>hello</finalName>
</build>
<profiles>
<profile>
<id>payara</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>fish.payara.maven.plugins</groupId>
<artifactId>payara-micro-maven-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>bundle</goal>
</goals>
</execution>
</executions>
<configuration>
<useUberJar>true</useUberJar>
<deployWar>true</deployWar>
<payaraVersion>5.182</payaraVersion>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
</project>
my web.xml (under webapp/WEB-INF):
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<data-source>
<name>java:global/hello</name>
<class-name>com.mysql.jdbc.Driver</class-name>
<url>jdbc:mysql://localhost:3306/connectiondatabase</url>
<user>root</user>
<password>mypass</password>
</data-source>
</web-app>
This database is running ok, and it was perfectly accessed from a general db client (dbeaver).
And finally, my persistence.xml (under resources/META-INF):
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
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="hello" transaction-type="JTA">
<jta-data-source>java:global/hello</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
I have a simple ping resource and JAXRS config class that came as default with the archtype I used to create the project (airhacks) and just one more simple entity, as follows:
package com.airhacks.entities;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
#Entity
#Table(name = "players")
#NamedQuery(name = Player.FIND_PLAYER_BY_NAME, query = "select player from Player player where player.name =:" + Player.PLAYER_PARAMETER)
public class Player {
public static final String PLAYER_PARAMETER = "Player.name";
public static final String FIND_PLAYER_BY_NAME = "Player.findByName";
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
At this point, I can deploy the app with no errors (mvn clean package payara-micro:start), although when checking the database, no table is created (I thought persistence-unit would take care of that with both 'exclude-unlisted-classes' and 'drop-and-create', but apparently, something else is needed).
After that, for testing purposes, I am adding a queryService class, so I can actually use my datasource.
I am adding the following class:
package com.airhacks.services;
import com.airhacks.entities.Player;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
#Stateless
public class QueryService {
#PersistenceContext(unitName = "hello")
EntityManager entityManager;
public Player getPlayerByName(String name) {
try {
return entityManager.createNamedQuery(Player.FIND_PLAYER_BY_NAME, Player.class)
.setParameter(Player.PLAYER_PARAMETER, name)
.getSingleResult();
} catch (NoResultException | NonUniqueResultException e) {
return null;
}
}
}
As far as I understand, there's no need to detail anything else on the PersistenceContext annotation, as we only have one persistence unit (although I also added unitName="hello", with the same result). But once I try to deploy the app, I get the following errors:
[2021-02-13T13:50:07.023+0000] [] [SEVERE] [NCLS-CORE-00026]
[javax.enterprise.system.core] [tid: _ThreadID=1 _ThreadName=main]
[timeMillis: 1613224207023] [levelValue: 1000] [[ Exception during
lifecycle processing javax.persistence.PersistenceException: Exception
[EclipseLink-4002] (Eclipse Persistence Services - 2.7.1.qualifier):
org.eclipse.persistence.exceptions.DatabaseException Internal
Exception: java.sql.SQLException: Error in allocating a connection.
Cause: The driver could not be loaded: com.mysql.jdbc.Driver Error
Code: 0
...
2.7.1.qualifier): org.eclipse.persistence.exceptions.DatabaseException Internal
Exception: java.sql.SQLException: Error in allocating a connection.
Cause: The driver could not be loaded: com.mysql.jdbc.Driver Error
Code: 0
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:316) ...
... 41 more Caused by: java.sql.SQLException: Error in allocating a connection. Cause: The driver could not be loaded:
com.mysql.jdbc.Driver
... com.sun.appserv.connectors.internal.api.PoolingException:
So, basically, stating that the driver could not be loaded.
And this is where I can't do much. My external libs contain indeed mysql, so I don't understand where this problems comes from. I already tried with different db (SQLite, for example), but I get the very same problem, with different drivers, of course.
Would really appreciate your help and sorry for the long post.
Thank you!
This version of Mysql uses another driver class, change you web.xml from:
<class-name>com.mysql.jdbc.Driver</class-name>
to:
<class-name>com.mysql.cj.jdbc.MysqlConnectionPoolDataSource</class-name>
Reference: https://docs.payara.fish/community/docs/5.2020.5/documentation/payara-server/jdbc/mysql-connectorj-8-changes.html
For some reason, DB doesn't receive the values it's given via a table parameter. It sees correct count of rows in the table, and also the given count of columns is correct (else I get an error for mismatch), and yet values themselves are null.
DB version (SELECT * FROM V$VERSION):
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
"CORE 12.1.0.2.0 Production"
TNS for 64-bit Windows: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production
Tested with oracle drivers ojdbc6 (version 11.2.0.4), ojdbc7 (version 11.2.0.4), ojdbc7 (version 12.1.0.2).
This is signature of the DB procedure:
Procedure Send_Message_Test (
i_Receiver_List_Users_Tbl In Receiver_List_Users_Tbl
);
the types:
CREATE OR REPLACE Type Receiver_List_Users_Rt Force As Object (
User_Id Varchar2(30 Char)
)
/
CREATE OR REPLACE Type Receiver_List_Users_Tbl Is Table Of Receiver_List_Users_Rt
This is minimal complete Java Spring Boot application to invoke it:
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.14.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
src/main/resources/application.properties
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:#192.168.1.233:1521/sis1
spring.datasource.username=<omitted>
spring.datasource.password=<omitted>
spring.datasource.tomcat.test-while-idle=true
spring.datasource.tomcat.test-on-borrow=true
spring.datasource.tomcat.test-on-return=false
spring.datasource.tomcat.validation-query=select 1 from dual
spring.datasource.tomcat.max-active=100
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.remove-abandoned-timeout=60
spring.datasource.tomcat.remove-abandoned=true
spring.datasource.tomcat.log-abandoned=true
src/main/java/com/test/test/TestApplication.java
package com.test.test;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Statement;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.datasource.DataSourceUtils;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
#SpringBootApplication
public class TestApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Autowired
DataSource dataSource;
#Override
public void run(ApplicationArguments args) throws Exception {
Connection conn = DataSourceUtils.getConnection(dataSource);
CallableStatement callStmt = null;
Statement alterDateFormatStmt = conn.createStatement();
alterDateFormatStmt.execute("alter session set NLS_DATE_FORMAT = 'YYYY-MM-DD'");
alterDateFormatStmt.close();
// create PLSQL procedure statement
String stmStr = "{call Notification_Manage_v2.Send_Message_Test (?)}";
// create Oracle statement and set parameters
callStmt = conn.prepareCall(stmStr);
StructDescriptor recordDescriptor = StructDescriptor.createDescriptor("RECEIVER_LIST_USERS_RT",
callStmt.getConnection());
ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("RECEIVER_LIST_USERS_TBL",
callStmt.getConnection());
callStmt.setObject(1, new ARRAY(arrayDescriptor, callStmt.getConnection(),
new STRUCT[] { new STRUCT(recordDescriptor, callStmt.getConnection(), new Object[] { "test" }) }));
callStmt.execute();
}
}
However, after running this, this is what I see in Transactions table Param_values column:
i_Receiver_List_Users_Tbl => Receiver_List_Users_Tbl(Receiver_List_Users_Rt ())
Any insight would be much appreciated.
This is how from DB side the param_values column is formed:
CREATE OR REPLACE Type Receiver_List_Users_Rt Force As Object (User_Id Varchar2(30 Char));
CREATE OR REPLACE Type Receiver_List_Users_Tbl Is Table Of Receiver_List_Users_Rt;
Procedure Send_Message_Test (i_Receiver_List_Users_Tbl In Receiver_List_Users_Tbl
) Is
--
tbl_Receiver_List_Users Receiver_List_Users_Tbl := Receiver_List_Users_Tbl();
v_Param_Receiver_List_Users Varchar2(3000);
--
Begin
--
For e_Usr In (Select t_Receiver_List_Users.User_Id User_Id
From Table(i_Receiver_List_Users_Tbl) t_Receiver_List_Users
) Loop
--
v_Param_Receiver_List_Users := v_Param_Receiver_List_Users ||
Case When v_Param_Receiver_List_Users Is Not Null Then ', ' End||
'Receiver_List_Users_Rt ('||e_Usr.User_Id||')';
--
End Loop;
--
If v_Param_Receiver_List_Users Is Not Null Then
v_Param_Receiver_List_Users := 'Receiver_List_Users_Tbl('||v_Param_Receiver_List_Users||')';
End If;
--
--
dbms_output.put_Line('i_Receiver_List_Users_Tbl => '||v_Param_Receiver_List_Users); -- !!!!!!!! no values receive
--
--
End;
My guess is that you are not correctly creating nor populating the IN parameter of the PL/SQL stored procedure. Below is a code snippet taking parts of the code you posted ("TestApplication.java") and adding code that I hope will solve your problem.
EDITED
Connection conn = DataSourceUtils.getConnection(dataSource); // your code
Object[] attributes = new Object[1];
attributes[0] = "Test";
java.sql.Struct obj = conn.createStruct("Receiver_List_Users_Rt", attributes);
Object[] elems = new Object[1];
elems[0] = obj;
oracle.jdbc.OracleConnection oraConn = (oracle.jdbc.OracleConnection) conn;
java.sql.Array objs = oraConn.createARRAY("Receiver_List_Users_Tbl", elems);
callStmt.setArray(1, objs);
callStmt.execute(); // your code
Unsatisfying closure, but DB personnel tore down the environment, made a new one, and it started working. So the issue wasn't with java or driver side. Whatever went wrong with DB remains an unrecoverable mystery
Anybody know why it doesn't work?
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
06/04/2017 14:11:24.732 ERROR [main] - org.springframework.boot.SpringApplication: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
at com.cadit.web.WebApplicationAware.main(WebApplicationAware.java:19)
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.util.Assert.notEmpty(Assert.java:277)
at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.<init>(JpaMetamodelMappingContext.java:52)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:71)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:26)
at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:134)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 16 common frames omitted
I defined entities in com.cadit.entities:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="TEST")
public class GenericBeans implements BeanType, IEntity<Long> {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "TEST_PAID")
protected Long id;
#Column(name = "SOCIETA")
private String SocietaCod;
#Column(name = "CONTO_INTERMEDIARIO")
private String contoInt;
#Column(name = "TIPO_OPERAZIONE")
private String tipoOpe;
public GenericBeans(String societaCod, String contoInt, String tipoOpe) {
SocietaCod = societaCod;
this.contoInt = contoInt;
this.tipoOpe = tipoOpe;
}
public GenericBeans() {
}
public String getSocietaCod() {
return SocietaCod;
}
public void setSocietaCod(String societaCod) {
SocietaCod = societaCod;
}
public String getContoInt() {
return contoInt;
}
public void setContoInt(String contoInt) {
this.contoInt = contoInt;
}
public String getTipoOpe() {
return tipoOpe;
}
public void setTipoOpe(String tipoOpe) {
this.tipoOpe = tipoOpe;
}
#Override
public String toString() {
return "CSV [SocietaCod=" + SocietaCod + ", contoInt=" + contoInt + ", tipoOpe=" + tipoOpe + "]";
}
#Override
public Long getId() {
return this.id;
}
#Override
public void setId(Long id) {
this.id=id;
}
}
I definied my datasource entry definition for spring:
import org.apache.log4j.Logger;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan
#EntityScan("com.cadit.entities")
//#EnableJpaRepositories("com.cadit.entities")
#EnableTransactionManagement
#PropertySource("classpath:db-config.properties")
public class DbAutoConfiguration {
static final Logger logger = Logger.getLogger(DbAutoConfiguration.class);
public DbAutoConfiguration() {
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
//DataSource ds =new EmbeddedDatabaseBuilder().addScript("classpath:sql/schema.sql").addScript("classpath:testdb/data.sql").build();
DataSourceBuilder ds = DataSourceBuilder.create();
logger.info("dataSource = " + ds);
return ds.build();
}
}
My db-config.properties is:
spring.jpa.hibernate.ddl-auto: validate
spring.jpa.hibernate.naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
#spring.jpa.database: SQL
spring.jpa.show-sql: true
spring.datasource.driverClassName=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://localhost:1433;databaseName=example
spring.datasource.username=xxx
spring.datasource.password=xxx
IEntity is:
public interface IEntity <I extends Serializable> extends Serializable{
/**
* Property rappresenta la primary key.
*/
String P_ID = "id";
/**
* restituisce la primary key
* #return
*/
I getId();
/**
* imposta la primary key
* #param id
*/
void setId(I id);
}
I try to write CSV file to database using CrudRepository interface of spring:
import java.io.File;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.repository.CrudRepository;
import com.cadit.entities.GenericBeans;
import com.csvreader.CsvReader;
public class CsvReaders {
static final Logger logger = Logger.getLogger(CsvReader.class);
#Autowired
public CrudRepository<GenericBeans,Long> _entitymanager;
public List loadDataFromCsv(String fileName) {
try {
File file = new ClassPathResource(fileName).getFile();
CsvReader csv = new CsvReader(file.getAbsoluteFile().getPath(),';');
csv.readHeaders();
List l = new LinkedList();
GenericBeans b = new GenericBeans ();
while (csv.readRecord())
{
b.setSocietaCod(csv.get(0));
b.setContoInt(csv.get(1));
b.setTipoOpe(csv.get(2));
_entitymanager.save(b); //persist on db
l.add(b);
b = new GenericBeans();
}
b=null;
return l;
} catch (Exception e) {
logger.error("Error occurred while loading object list from file " + fileName, e);
return Collections.emptyList();
}
}
}
I DO NOT use main class but a class which extend SpringBootServletInitializer because i want to run it on both standalone tomcat and Tomcat installation as WAR application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages={"com.cadit.entities","com.cadit.beans"})
#EnableAutoConfiguration
public class WebApplicationAware extends SpringBootServletInitializer {
private static Class<WebApplicationAware> applicationClass = WebApplicationAware.class;
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
}
All properties file are in classpath resources because it's a maven project.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>xxxx</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.1.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
<!-- altre dipendenze non spring -->
<!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
<!-- per jpa solo se si usa il Tomcat embedded -->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- end -->
<!-- dipendenze logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<!-- fine dip logback -->
</dependencies>
<properties>
<start-class>hello.WebApplicationAware</start-class>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
What's the problem, why doesn't it find JPA entities when I run WebApplicationAware class?
Spring does not find any JPA Entities, so no JPA Meta Model is created, that is why you face the exception.
The cause of this problem may be a wrong persistence-api version on your class path.
You are using
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
but I am pretty shure your spring version uses persistence-api version 2.
Could it be, you are using #Entity annotation from version 1 ?
At runtime spring uses version 2, and this is searching for Entites using #Entity from version 2 only !
Remove the dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.1.RELEASE</version>
</dependency>
Instead add
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
This will give you all JPA dependencies in the right version.
I solved it by adding 2 annotations
#EnableAutoConfiguration
#EntityScan(basePackages = { "com.wt.rds" })
and my dependency was in gradle
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.4.RELEASE'
Unfortunately, most of the springboot guides on JPA integration test often lack a piece of configuration here and there.
So here is an example that hopefully should just work for you.
Point 1.
My local environment is currently setup to use springboot version:
<version.spring.boot>1.5.9.RELEASE</version.spring.boot>
That being said, I am currently setting up my local environment to be able to run integration tests against multiple databases (e.g. postgres, hsql, h2).
Therefore, I start by googling any random toturial that approaches this problem.
The next link is one such example:
https://www.baeldung.com/spring-testing-separate-data-source
The above example is a good starting point. It allows you to scoop up a valid Entity and a Valid repository. The springboot test class itself, on the other hand, leaves a lot ot be desired.
With the above example, you will immediately struggle with the integration test. You will get the usuable problems about the example not giving you the application.class to configure the integration test, and you are left hanging clueless as to what springboot annotations you need to put "where" to make the test to finally run without explosions.
So now I give you a MINIMAL set of 3 classes (Entity + Repository + SpringbootTest) that should hopefully have 100 percent of the configuration you need. This will serve as a basis of any JPA based integration test you will need to do in the future, then you can swap your entities and repositories, and continue testing with the same type of srpingboot configuration.
I start by giving you the IRRELEVANT classes. The stuff that is always the same, the stuff that you want to test, and that has nothing to do with configuration.
I am referring to REPOSITORY + ENTITY.
In eclipse create your java package:
tutorial.www.baeldung.com.tutorial001jpa.separateDS
Dump into this package the following trivial entity and repository classes, that are based on the tutorial reference I gave above.
Tutorial001GenericEntity
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "TUTORIAL_001_GENERIC_ENTITY")
public class Tutorial001GenericEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
public Tutorial001GenericEntity() {
super();
}
public Tutorial001GenericEntity(String value) {
super();
this.value = value;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
// standard constructors, getters, setters
}
Then we go for the second trivial code snippet.
The spring repository boiler plate code.
Tutorial001GenericEntityRepository
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import org.springframework.data.jpa.repository.JpaRepository;
public interface Tutorial001GenericEntityRepository extends JpaRepository<Tutorial001GenericEntity, Long> {
}
At this point your maven project, src/test/java has a total of two classes. The basic stuff.
An entity and a repository, that serve as an example of any integration test you will ever need to do.
So now you go to the only important class in the example, the stuff that always gives a lot of problems, and that is the springboot test class which more then being responsible to test your business logic also has the complex task of CONFIGURING your test.
In this case, this test class has ALL IN ONE the annotations that allow springboot to disocver your entities, repositories, etc...
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
#SpringBootTest()
public class Tutorial001GenericEntityIntegrationTest {
#EntityScan(basePackageClasses = { Tutorial001GenericEntity.class })
#EnableJpaRepositories(basePackageClasses = Tutorial001GenericEntity.class)
#EnableAutoConfiguration()
public static class ConfigureJpa {
}
#Autowired
private Tutorial001GenericEntityRepository genericEntityRepository;
#Test
public void givenTutorial001GenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
Tutorial001GenericEntity genericEntity = genericEntityRepository.save(new Tutorial001GenericEntity("test"));
Tutorial001GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId());
assertNotNull(foundEntity);
assertEquals(genericEntity.getValue(), foundEntity.getValue());
}
}
The important thing, you see, is that this spring boot test has a class level annotation to provide to the springboot test the configuration context.
What we are doing is dumping one and only one class reference that represents our test configuration.
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class
And then on this little guy, you put all of the additional annotations in the world you need that springboot offers to configure applications.
In this case we have a dedicated annotation to mention entities.
Another to mention repositories.
And another to tell springboot to activate its auto configuration.
This springboot auto configuration annotation then does additional vodoo, like looking at your classpath and seeing that you have in the classpath say:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
<version>2.3.4</version>
</dependency>
And it will immediately know how to configure an in memory data source for this database.
Behind the scenes, there might be additional configuration that is getting used.
For example, if you create an application.properties file in your src/test/resources that file will be considered.
It is very to see that the appliction.properties is considered by your running test.
If you want to verify this, make sure that in your test setup you do not have, for example, any dependency on the JDBC driver for postgres.
And then put into your application.properties something liek this:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
This dialect is not compatible with HSQL or H2, so it will immediately make your green passing integration test blow up.
To be honest, I do not know if there is a simpler combo of annotations to properly configure the springboot scanning for an integration test.
As a rule, I would recommend that you try avoiding having hundreds of thousands of configuration classes in your src/test/resources.
Because if at some point you want to toggle all of your integration tests from using applicat-postgres.proeprties to application-hsql.properties, you might find yourself needing to tweak multiple configuration classes instead of just one.
So as rule, per maven component you write, I would try to have the tests that check repositories extend some sort of MyBaseINtegrationTestClass, and in there put this
#ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
So that you only need to play with one configuration for testing for the hole project.
IN any case, hopefully the triplet of classes given here helps you.
One finel thing, for maven dependencies for integration testing, here is what I am using:
<!-- Test Dependencies JPA REPOSITORY TESTS -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
The reason why i am using hsql and h2 is beacuse I want my integration tests to be able to be tunned to either use application-hsql or application-h2.properties.