nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" already exists; SQL statement: - spring

I've tried to create JPA small demo project, I faced this problem
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'personJdbcDao': Unsatisfied dependency expressed through field 'jdbcTemplate'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:/D:/Learning/c5/springframework/target/classes/schema.sql]: create table person ( ID integer not null, PERSON_NAME varchar(255) not null, LOCATION varchar(255), BIRTH_DATE timestamp, primary key(ID) ); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" already exists; SQL statement:
create table person ( ID integer not null, PERSON_NAME varchar(255) not null, LOCATION varchar(255), BIRTH_DATE timestamp, primary key(ID) ) [42101-214]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.24.jar:5.3.24]
Below my config
I made schema.sql for my H2 in the memory database.
src/main/resources/schema.sql
create table person
(
ID integer not null,
PERSON_NAME varchar(255) not null,
LOCATION varchar(255),
BIRTH_DATE timestamp,
primary key(ID)
);
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(1, 'Fazal', 'Peshawar',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(2, 'Haroon', 'Islamabad',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(3, 'Khan', 'Lahore',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(4, 'Khan', 'Islamabad',NOW());
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Main class:
#SpringBootApplication
public class DemoJPAApplication implements CommandLineRunner {
private Logger logger = LoggerFactory.getLogger(DemoJPAApplication.class);
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoJPAApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
logger.info("User id 1 -> {}", personRepository.findById(1));
}
}
Repository Class
#Repository
#Transactional
public class PersonRepository {
#PersistenceContext
EntityManager entityManager;
public Person findById(int id){
return entityManager.find(Person.class, id);
}
}
enitity class, here i use lombok
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
private Integer id;
#Column(name = "person_name")
private String personName;
#Column(name = "location")
private String location;
#Column(name = "birth_date")
private Date birthDate;
}
Could someone find what's wrong with my project? Really appreciate your help.
I run the problem and faced this problem

There is a small error that i face when i run this program.
The error said "Table 'PERSON' already exists" because we are already creating in schema.sql.
Spring boot auto configuration knows that we are using in-memory database H2 and that JPA is in the classpath. It also knows that I'm defining entities as well because i put #Entity in the Person class. So what it does is triggers schema update which is the hibernating feature, it automatically creates this schema for us.
so from now on, i don't need to define CREATE TABLE in schema.sql
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(1, 'Fazal', 'Peshawar',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(2, 'Haroon', 'Islamabad',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(3, 'Khan', 'Lahore',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(4, 'Khan', 'Islamabad',NOW());
i remove the CREATE TABLE PERSON from src/main/resources/schema.sql because the table would be created for me by schema update.
Schema update is triggered by a spring boot auto configuration and is one of the hibernate features.

Related

Strange validation conflict in Spring JPA TableGenerator

I have a legacy database with composite primary key in table project. (BaseEntity contains common properties for lastModifiedDate and lastModifiedBy)
#Entity
#IdClass(ProjectPk.class)
public class Project extends BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="nextProjectId")
#TableGenerator(
name="nextProjectId",
table="projectId",
pkColumnName = "proj_Id",
pkColumnValue="proj_id"
)
private Long projId;
#Id
private int version;
//other properties, getters and setters omitted for clarity
}
PK class
public class ProjectPk implements java.io.Serializable {
private int projId;
private int version;
//both constructoirs, equals, hashcode, getters and setters omitted for clarity
}
I have flyway migration files to simulate production database.
drop table if exists project;
CREATE TABLE project
(
proj_id bigint,
version int,
-- other columns omitted for clarity
PRIMARY KEY (`proj_id`, `version`)
) ENGINE=InnoDB;
drop table if exists project_id;
CREATE TABLE project_id
(
proj_id bigint
) ENGINE=InnoDB;
flyway creates tables as ordered in migration file
Table: project_id
Columns:
proj_id bigint
...
Table: project
Columns:
proj_id bigint PK
version int PK
...
during maven build I'm getting validation error
Schema-validation: wrong column type encountered in column [proj_id] in table [project_id]; found [bigint (Types#BIGINT)], but expecting [varchar(255) (Types#VARCHAR)]
What I did wrong to make hibernate expect [varchar(255) (Types#VARCHAR)]?
This is SpringBoot project 2.6.6 with MySql database
I see the following problems with your code:
Type mismatch between Project.projId (Long type) and ProjectPk.projId (int type).
You use wrong table structure for the project_id table.
You can see a working example below.
Assuming that you have the following tables:
CREATE TABLE test_project
(
proj_id bigint,
version int,
title VARCHAR(50),
PRIMARY KEY (proj_id, version)
);
create table table_identifier (
table_name varchar(255) not null,
product_id bigint,
primary key (table_name)
);
insert into table_identifier values ('test_project', 20);
and the following mapping:
#Entity
#Table(name = "test_project")
#IdClass(ProjectPk.class)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "nextProjectId")
#TableGenerator(
name="nextProjectId",
table="table_identifier",
pkColumnName = "table_name",
valueColumnName="product_id",
allocationSize = 5
)
#Column(name = "proj_id")
private Long projId;
#Id
private int version;
// other fields, getters, setters ...
}
you will be able to persist the entity like below:
Project project = new Project();
project.setVersion(1);
// ...
entityManager.persist(project);

Cannot access h2 user defined function in select clause but working in where clause

I have created a separate testing project to test this functionality with simple User entity having id and name attributes.
Schema.sql File
Create Table user (
id Int Not Null Primary Key Auto_Increment,
name Varchar(50) Default Null
);
Create Alias TEST FOR
"com.example.demospringbootproject.h2.CustomH2.customTestFunction";
Create Alias ANOTHERTEST FOR
"com.example.demospringbootproject.h2.CustomH2.customAnotherTestFunction";
CustomH2 Function class
package com.example.demospringbootproject.h2;
import com.example.demospringbootproject.model.User;
public class CustomH2 {
public static Integer customTestFunction(Integer id){
return id;
}
public static User customAnotherTestFunction(User user){
return user;
}
}
UserRepository
#Query("Select u From User u where TEST(u.id) = 1")
User repoFunction1();
#Query("Select ANOTHERTEST(u) From User u where u.id = 1")
User repoFunction2();
The first repo function is working fine
The second repo function is generating this error:
java.lang.IllegalArgumentException: Validation failed for query for method public
abstract com.example.demospringbootproject.model.User
com.example.demospringbootproject.repository.UserRepository.repoFunction2()!
I have tried renaming schema.sql to import.sql but same result.
My spring boot version: 2.5.7.
Java version: 1.8
H2 Dependency
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

Bidirectional mapping not working since it's not saving correctly

I am new to Spring and I am testing the API on postman and the instructor_id is not saving.
This is how I am testing it on postman and don't know if it is right or not.
{
"coursename":"tesasdasdast",
"description":"tesdasdsadasting",
"userEntity":{
"instructor_id":1
}
}
Steps:
I have two entities:
UserEntity
CourseEntity
I have a controller where I'm saving the information called:
CourseController
I have a repository called CourseRepo and I am extending JpaRepository
In CourseEntity:
#ManyToOne
#JoinColumn(name = "instructor_id")
private UserEntity userEntity;
In UserEntity:
#OneToMany(mappedBy = "userEntity", cascade=CascadeType.ALL)
private List<CourseEntity> courseEntity;
In my CourseController:
#PostMapping("/courses")
void addCourse(#RequestBody CourseEntity course) {
courseRepository.save(course);
}
I have added in my pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
This is the error message that I'm getting in postman:
{
"timestamp": "2019-10-10T16:51:47.780+0000",
"status": 500,
"error": "Internal Server Error",
"message": "org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.finaly.projectback.entity.CourseEntity.userEntity -> com.finaly.projectback.entity.UserEntity; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.finaly.projectback.entity.CourseEntity.userEntity -> com.finaly.projectback.entity.UserEntity",
"path": "/project/courses"
}
And these two are the tables in the database:
create table web_user(
id INT primary key auto_increment,
firstname VARCHAR(50),
lastname VARCHAR(50),
username VARCHAR(50),
email VARCHAR(50),
pwd VARCHAR(50),
user_role_id INT,
FOREIGN KEY (user_role_id) REFERENCES web_user_role(id)
);
CREATE TABLE web_course (
id INT PRIMARY KEY AUTO_INCREMENT,
coursename VARCHAR(50),
instructor_id INT,
FOREIGN KEY (instructor_id) REFERENCES web_user(id)
);
Thank you in advance for any advice.
Please check if you are correctly setting entities in associations.
userEntity.setCourseEntity and courseEntity.setUserEntity
Do you have this code in place and then check your hibernate queries that are fired on save method call.
I found my mistake. Instead of creating the foreign key as user_entity_user_id, i was creating it as user_id.
It seems like a stupid mistake but I'm still new to Spring and for now, many things look like magic.
Thanks everyone who tried to help.

How should I create H2 using SpringBoot?

I'm starting to play with Spring Boot and as part of that I want to create an in memory DB to work with and bootstrap with the application.
Given the config/code below I get no errors in the startup log and can access the application ok, so it does startup (I get template errors about objects not existing), but I don't get any data back from the DAO when calling findAll() (or if I try to call findById(int) ).
So while it seems things are ok (no error in log, log shows it finds the sql to create schema ad attempts to run the data.sql statements) when I try to access data via the DAO I get no Exception, but no data returned.
Any ideas or observations on the code that might be a problem?
I've added the Spring Data / H2 stuff to my pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
Spring DAO:
public interface PersonDao extends CrudRepository<Person, Integer> {
}
DB props in application.properties:
server.contextPath=/
server.port=8080
spring.mvc.view.suffix=.ftl
datasource.mine.jdbcUrl=jdbc:h2:tcp://localhost/mem:clubmanagement
datasource.mine.user=sa
datasource.mine.password=
datasource.mine.poolSize=30
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=DEBUG
spring.jpa.hibernate.ddl-auto=create
My service:
#Service
public class MemberServiceImpl implements MemberService {
#Autowired
PersonDao dao;
#Override
public Optional<ClubMember> getClubMember(int id) {
Person dbPerson = dao.findOne(id);
if(dbPerson == null) {
return Optional.empty();
}
return Optional.of(fromEntity(dbPerson));
}
#Override
public List<ClubMember> allMembers() {
Iterable<Person> people = dao.findAll();
List<ClubMember> members = new ArrayList<>();
people.forEach(person -> {
members.add(fromEntity(person));
});
return members;
}
private ClubMember fromEntity(Person p) {
ClubMember member = new ClubMember();
member.setCurrentGrade(p.getCurrentGrade());
member.setFirstName(p.getFirstName());
member.setLastName(p.getLastName());
member.setAssociationMemberId(p.getAssociationMemberId());
member.setLastGradingDate(p.getLastGradingDate());
return member;
}
}
Schema.sql in resources/ :
create table CLUB
(id int not null, name varchar(60), association_member_id int);
create table PERSON
(
id int not null, grade_id int, first_name varchar(35), last_name varchar(35),
association_membership varchar(12), last_grading_date date
);
create table GRADE
(id int not null, name varchar(20));
In data.sql (again in resources directory):
insert into club (id, name, association_member_id) values (1, 'some club', '123');
insert into person (id, grade_id, first_name, last_name, association_membership, last_grading_date)
values (1, 1, 'name', 'lastname', 'a1234', '2016-03-23');
Entity class I am trying to retrieve (Trying to use Lombock, also new to me, to generate getters/setters):
#Entity
#Table(name = "person")
public #Data class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#JoinColumn(name = "grade_id")
private GRADE currentGrade;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "association_membership")
private String associationMemberId;
#Column(name = "last_grading_date")
#Temporal(value = TemporalType.DATE)
private Date lastGradingDate;
}
you want to add H2 database, but you added HSQLDB, please replace
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
with
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
EDIT
I noticed you have multiple issues in your code:
default schema file name is schema.sql not Schema.sql
names of tables in schema.sql are different than names in data.sql (PERSON vs person)
you used this spring.jpa.hibernate.ddl-auto=create in application.properties (default option), in this case JPA databases schema only will be automatically created (without data creation), so data.sql will not be executed, to fix this issues you can use validate or update option
I will write one simple example how to use H2 database with spring boot and JPA
This is the project structure:
Grade Entity
package com.shijazi;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="GRADE")
public class Grade {
#Id
#GeneratedValue
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Grade() {
}
}
GradeRepository.java
package com.shijazi;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface GradeRepository extends JpaRepository<Grade, Integer> {
}
Application.java
#SpringBootApplication
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
private GradeRepository gradeRepo;
#RequestMapping(value="api/test")
public List<Grade> getall()
{
return gradeRepo.findAll();
}
}
application.properties
spring.jpa.hibernate.ddl-auto=validate
schema.sql
create table GRADE (id int not null, name varchar(20));
data.sql
insert into GRADE (id, name) values (2, 'name');
Dependencies in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Now just run application and call this URL: http://localhost:8080/api/test
try to change the spring.jpa.hibernate.ddl-auto and see results
if you activate ddl-auto and have a schema.sql, BOTH of them are executed. But normally schema.sql is executed first. So the ddl-auto throws everything away, which was created by schema.sql and data.sql
After spending some time working through some ideas with #Safwan Hijazi in chat, came to the conclusion that what is happening is that the schema.sql and data.sql were being run but then the schema was recreated depending on the value (or lack of) of the spring.jpa.hibernate.ddl-auto property.
If not specified, spring/hibernate between them ended up recreating an empty schema (default seems to be create-drop for in memory DB).
If set the 'none' then that wouldn't happen and DB as created by the schema and data sql scripts would remain and the application functioned correctly.
See also: CrudRepository not reading data from schema.sql
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Just gives you an spring boot opinionated inclusion to the spring-boot-starter-data-jpa maven file, for the bill of material of all the dependencies. To use any one of the dependency defined in the Dependency management of the spring-boot-starter-data-jpa pom you will have to explicitly declare the dependency in dependency section of your pom file.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
To get the h2 database up and running with your application, you can specifiy the properties in your application.properties file at src/main/resources/application.properties use:
spring.h2.console.enabled=true
spring.h2.console.path=/h2DB
so when you run your application with spring application starter, you will be able to access the application at http://localhost:8080/h2DB login to the DB and you can verify if the database had the inserts in it or not?
Don't find the data in there then you know where to make a change to keep the data there.

Entity primary key and sequence

i use oracle and openjpa.
i have an primary key and i want to use a sequence for its value
CREATE TABLE LOG (
ID NUMBER(10) not null,
TIMESTAMP TIMESTAMP DEFAULT (SYSDATE),
constraint PK_ID PRIMARY KEY (ID)
);
CREATE SEQUENCE ID_SEQ
START WITH 1
INCREMENT BY 1
NOCACHE
NOCYCLE;
#Entity
#Table(name="Log")
public class Log implements Serializable {
#Id
#SequenceGenerator(name="SEQ_GEN", sequenceName="ID_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN" )
#Column(name="id",nullable=false)
private long id;
#Column(name="timestamp",nullable=false)
private Timestamp timestamp;
public Log(){
}
public Log(Timestamp timestamp){
this.timestamp = timestamp;
}
..
}
#Stateless
public class LogDAO {
#PersistenceContext(unitName="logEntityPU")
private EntityManager em ;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean insert(Log log){
em.persist(log);
return true;
}
...
...
}
when i check my Log object, id=0 and the timestamp is ok
but i get this error
ORA-01400: integrity constraint violation: NOT NULL check constraint.Insertion of null value not allowed
it's like jpa don't do the link with the sequence.
when i debug more, i see this error: javax.persistence.TransactionRequiredException: No active transaction for PuId=
any idea?
A trigger like this will insert the primary key if one is not provided by the app
CREATE OR REPLACE TRIGGER YOUR_SCHEMA.TRG_YOUR_TABLE
BEFORE INSERT
ON YOUR_SCHEMA.YOUR_TABLE
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
PrimaryKeyId NUMBER;
BEGIN
IF :NEW.PRIMARY_KEY_ID IS NULL THEN
SELECT your_schema.seq_your_table.nextval
INTO :NEW.PRIMARY_KEY_ID
FROM dual;
END IF;
END YOUR_SCHEMA.TRG_YOUR_TABLE;
Then do a
Select YOUR_SCHEMA.seq_your_table.currval from dual;
to get the value

Resources