Liquibase and spring jpa - spring

I'm having problem with deploy postgres on a linux environment though I don't sure it's related.
Linux version: 9.3.11
Windows version: 9.5
The error that I get:
2016-03-15_19:19:40.478 [http-nio-9090-exec-3] WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: 42P01
2016-03-15_19:19:40.479 [http-nio-9090-exec-3] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: relation "rbac_roles" does not exist
Position: 125
2016-03-15_19:19:40.520 [http-nio-9090-exec-3] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
org.postgresql.util.PSQLException: ERROR: relation "rbac_roles" does not exist
Position: 125
On my windows environment table definition created by Liquibase looks as following:
-- Table: public.rbac_roles
-- DROP TABLE public.rbac_roles;
CREATE TABLE public.rbac_roles
(
tenantid character varying(255) NOT NULL,
id integer NOT NULL DEFAULT nextval('rbac_roles_id_seq'::regclass),
name character varying(255) NOT NULL,
urlprefix character varying(255),
CONSTRAINT pk_rbac_roles PRIMARY KEY (id),
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.rbac_roles
OWNER TO postgres;
On my linux (the prblematic) environment table definition created by Liquibase looks as following:
-- Table: public.rbac_roles
-- DROP TABLE public.rbac_roles;
CREATE TABLE public.rbac_roles
(
tenantid character varying(255) NOT NULL,
id serial NOT NULL,
name character varying(255) NOT NULL,
urlprefix character varying(255),
CONSTRAINT pk_rbac_roles PRIMARY KEY (id),
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.rbac_roles
OWNER TO postgres;
The Spring Jpa object looks like this:
#Entity(name = "rbac_roles")
public class Role implements HasTenantId {
#Id
#SequenceGenerator(name="roles_seq", sequenceName = "rbac_roles_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "roles_seq")
private int id;
...
The Liquibase config looks like this:
<createTable tableName="rbac_roles">
<column name="tenantid" type="varchar(255)">
<constraints primaryKeyName="pk_roles" nullable="false" />
</column>
<column name="id" autoIncrement="true" type="integer">
<constraints primaryKey="true" nullable="false" />
</column>
...
</createTable>
The table rbac_roles created successfully but ID looks diffrent
What I'm doing wrong, why the behavior is different?

In your JPA Annotations you are using a sequence generator, but in your changelog there is an autoincrement type for ID attribute
<createTable tableName="rbac_roles">
<column name="tenantid" type="varchar(255)">
<constraints primaryKeyName="pk_roles" nullable="false" />
</column>
<!-- HERE you specify autoincrement -->
<column name="id" autoIncrement="true" type="integer">
<constraints primaryKey="true" nullable="false" />
</column>
...
</createTable>
It is confusing, and I don't know why this code is generating different tables on windows and linux, maybe your postgresql is not the same version ...
Please, fix your changelog and put the following
<createTable tableName="rbac_roles">
<column name="tenantid" type="varchar(255)">
<constraints primaryKeyName="pk_roles" nullable="false" />
</column>
<!-- Removed autoincrement -->
<column name="id" type="integer">
<constraints primaryKey="true" nullable="false" />
</column>
...
</createTable>
<!-- HERE I specify sequence -->
<createSequence sequenceName="rbac_roles_id_seq" incrementBy="1"/>
This way, it should generate same table on both systems.

Related

How to get db information using Spring JdbcTemplate and plain SQL (Micronaut)

I am trying to create a web application using Micronaut and need to get information from travelLog table using Spring JdbcTemplate and plain
SQL. I was using this tutorial https://www.greggbolinger.com/posts/using-springs-jdbctemplate-with-micronaut/ to solve this, but I faced the following problem:
30.869 [default-nioEventLoopGroup-1-2] ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: StatementCallback; bad SQL grammar [SELECT * FROM travelLog]; nested exception is org.postgresql.util.PSQLException: ERROR: relation "travellog" does not exist
Position: 15
org.springframework.jdbc.BadSqlGrammarException: StatementCallback; bad SQL grammar [SELECT * FROM travelLog]; nested exception is org.postgresql.util.PSQLException: ERROR: relation "travellog" does not exist
Position: 15
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:237)
at
Here is travelLog table Schema
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="01" author="julia">
<createTable tableName="travelLog"
remarks="A table to contain all travel logs">
<column name="id" type="int">
<constraints nullable="false" unique="true" primaryKey="true"/>
</column>
<column name="date" type="timestamp">
<constraints nullable="false"/>
</column>
<column name="regNumber" type="varchar">
<constraints nullable="false"/>
</column>
<column name="ownersName" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="odometerValueBeg" type="int">
<constraints nullable="false"/>
</column>
<column name="odometerValueEnd" type="int">
<constraints nullable="false"/>
</column>
<column name="departurePlace" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="destinationPlace" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="description" type="varchar">
</column>
</createTable>
</changeSet>
</databaseChangeLog>
Here is JdbcTemplateFactory.java
#Factory
public class JdbcTemplateFactory {
#Inject
DataSource dataSource;
#Bean
#Singleton
JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
}
Here is TravelLogService.java
#Singleton
#Requires(beans = JdbcTemplate.class)
public class TravelLogService {
private final JdbcTemplate jdbcTemplate;
public TravelLogService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Transactional
public void printUsernames() {
jdbcTemplate.query("SELECT * FROM travelLog", (rs) -> {
System.out.println(rs.getString("ownersName"));
});
}
To create and query case sensitive table, columns etc, names must be quoted like this:
SELECT * FROM "travelLog"
For liquibase settings check this answer https://stackoverflow.com/a/60654633/1854103
Also please consider change your naming conventions

OneToMany relation cannot be saved in spring with liquibase

I am trying to build a data model in spring that cascades up to 3 levels down with one-to-many relationships, but I cannot get it to work with liquibase scripts.
I am using spring boot with Kotlin and liquibase with a PostgreSQL database.
What I did so far:
cutting down the code to only contain the part that does not work (see below)
I tried both #OneToMany with #JoinTable as well as with #JoinColumn, and I also tried the same with #ManyToMany to rule out an issue with #OneToMany
I ran the same code (below) without liquibase to have Hibernate/JPA create the tables from the models
this actually worked, so I generated liquibase scripts out of these tables, but they look exactly the same as my own (except key names)
retrieving data with these models work (if I insert data via SQL directly)
To be honest I am not sure, if the issue is in the model, in the configuration, or in the liquibase scripts, so I'll just post all of those. Am I missing a configuration? Did I configure the cascading correct? Are my model definitions/liquibase scripts wrong?
The exception I get on saving a parent is:
Hibernate: insert into parent (name) values (?)
2021-12-15 23:29:16.797 WARN 14115 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23502
2021-12-15 23:29:16.798 ERROR 14115 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: null value in column "id" of relation "parent" violates not-null constraint
Detail: Failing row contains (null, Test 1).
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [id" of relation "parent]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "id" of relation "parent" violates not-null constraint
Detail: Failing row contains (null, Test 2).
The code I am trying to run:
val parent = Parent(
id = 0,
name = "Test 2"
).apply {
children = mutableSetOf(
Child(
id = 0,
name = "Test 21",
parent = this
).apply {
grandchildren =
mutableSetOf(
Grandchild(
id = 0,
name = "Test 211",
child = this
)
)
},
Child(
id = 0,
name = "Test 22",
parent = this
)
)
}
val saveParent: Parent = parentRepository.save(parent)
models:
#Entity
class Parent(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0,
var name: String,
#OneToMany(mappedBy = "parent", cascade = [CascadeType.ALL])
var children: MutableSet<Child> = mutableSetOf()
)
#Entity
class Child(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0,
var name: String,
#ManyToOne #JoinColumn(name = "child_id")
var parent: Parent,
#OneToMany(mappedBy = "child", cascade = [CascadeType.ALL])
var grandchildren: MutableSet<Grandchild> = mutableSetOf()
)
#Entity
class Grandchild(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0,
var name: String,
#ManyToOne #JoinColumn(name = "child_id")
var child: Child
)
application.yml
spring:
datasource:
platform: postgres
url: jdbc:postgresql://localhost:5432/onetomany?ssl=false
driver-class-name: org.postgresql.Driver
initialization-mode: always
jpa:
database: postgresql
database-platform: org.hibernate.dialect.PostgreSQLDialect
generate-ddl: false
hibernate:
ddl-auto: none
liquibase:
enabled: true
change-log: classpath:db/master.xml
liquibase script:
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet author="bruce (generated)" id="data-1">
<createTable tableName="parent">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK_PARENT"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="bruce (generated)" id="data-2">
<createTable tableName="child">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK_CHILD"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="parent_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="bruce (generated)" id="data-3">
<createTable tableName="grandchild">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK_GRANDCHILD"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="child_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="bruce (generated)" id="data-6">
<addForeignKeyConstraint baseColumnNames="parent_id" baseTableName="child" constraintName="FK_CHILD_PARENT"
deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="parent" validate="true"/>
</changeSet>
<changeSet author="bruce (generated)" id="data-8">
<addForeignKeyConstraint baseColumnNames="child_id" baseTableName="grandchild" constraintName="FK_CHILD_GRANDCHILD"
deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
referencedColumnNames="id" referencedTableName="child" validate="true"/>
</changeSet>
</databaseChangeLog>
#GeneratedValue(strategy = GenerationType.IDENTITY) would normally work by specifying an auto-incrementing default value at the database, such as nextval('my_entity_sequence'::regclass). Upon insert, the DB will generate the identifier.
In Postgres, there are serial/bigserial pseudo-types to specify an auto-increment column (which will internally create the sequence as well as the column default value), so the DDL could e.g. look like this:
create table my_entity ( id bigserial not null, primary key (id) )
https://www.postgresql.org/docs/current/datatype-numeric.html
In your case, liquibase missed the type/defaults for all of the ID columns (right now just a "parent"-insert fails, but inserts for the other entities will fail as well).
This is a known liquibase issue: https://github.com/liquibase/liquibase/issues/1009 - suggestions to work around it include manually specifying autoIncrement="true" in the changeset.

Liquibase , how to add a group of fields like primary key?

I have an entity with a group of fields in primary key.
Like this :
#Entity
#Table(name = "pv_object")
#NamedQuery(name = "PreviousObject.findAll", query = "SELECT p FROM PreviousObject p")
public class PreviousObject implements Serializable {
#EmbeddedId
private FieldsDTO fieldsdto;
//
}
FieldsDTO class contains 2 String and 2 Integer.
I have and I use Liquidbase on my project in a XML file, but, I don't know how to represent this ID of 4 fields in liquidbase.
Thanks for your help :)
In <addPrimaryKey you can configure columnNames by all your columns that compose your primary key
<changeSet author="liquibase-docs" id="addPrimaryKey-example">
<addPrimaryKey
columnNames="id, name"
constraintName="pk_person"
schemaName="public"
tableName="person"
tablespace="A String"/>
</changeSet>
Assign the same primaryKeyName to them.
<createTable tableName="pv_object">
<column name="x" type="bigint">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK_pv_object"/>
</column>
<column name="y" type="bigint">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK_pv_object"/>
</column>
</createTable>
or add separately
<addPrimaryKey tableName="REPRESENTATIVE" columnNames="REPRESENTED_USER_ID,REPRESENTATIVE_ID"
constraintName="REPRESENTED_REPRESENTATIVE_PK" />

spring-boot with liquibase #OneToMany mapping

I have two entity Person and Address. And Person can have multiple Address.
<createTable tableName="ADDRESS">
<column name="id" type="bigint(20)" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
... //columns
</column>
</createTable>
<createTable tableName="PERSON">
<column name="id" type="bigint(20)" autoIncrement="true">
<constraints primaryKey="true" nullable="false" />
... //columns
</column>
</createTable>
<addForeignKeyConstraint
constraintName="fk_constraint_worker_phone_number"
referencedTableName="CONTACT_NUMBER" baseColumnNames="ContactNumbers"
baseTableName="WORKER" referencedColumnNames="id" />
I want 3rd table (like hibernate generate in #OneToMany mapping).
How to do this with liquibase-springboot?
If the relation is truly a OnToMany, you don't need a 3rd table. Simply, add PrimaryKeyJoinColumn.
If the address can be reused for many persons, it's a ManyToMany relation.
You can use #ManytoMany and add information about you joined table un #jointable
Well, in case of liquibase we have to create the 3rd table manually and have to apply the necessary constraints.
Create the table which manages the mapping :
<createTable tableName="PERSON_ADDRESS">
<column name="PERSON_ID" type="BIGINT">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="ADDRESS_ID" type="BIGINT">
<constraints primaryKey="true" nullable="false" />
</column>
</createTable>
Apply the constraints:
1) Ensure that Persons id is unique in the mapping table
2) A foreign key relationship between ADDRESS's id and PERSON_ADDRESS's PERSON_ID
3) A foreign key relationship between PERSON's id and PERSON_ADDRESS's ADDRESS_ID
<addUniqueConstraint
columnNames="PERSON_ID" tableName="PERSON_ADDRESS"
constraintName="UK_PHONE_NUMBERS_ID" />
<addForeignKeyConstraint
constraintName="FK_ADDRESS_PERSON_ADDRESS"
referencedTableName="ADDRESS"
baseColumnNames="ADDRESS_ID"
baseTableName="PERSON_ADDRESS" referencedColumnNames="id" />
<addForeignKeyConstraint
constraintName="FK_PERSON_PERSON_ADDRESS"
referencedTableName="PERSON"
baseColumnNames="PERSON_ID"
baseTableName="PERSON_ADDRESS" referencedColumnNames="id" />

How to create conditional unique constraint over nullable columns in with hibernate mapping file?

I have these tables in oracle database:
create table APP_BASEINFORMATIONHEADER
(
id NUMBER(10) not null,
topic VARCHAR2(50 CHAR) not null,
subsystem_type NUMBER(10)
);
create table APP_BASEINFORMATION
(
id NUMBER(10) not null,
code NUMBER(10),
parentid NUMBER(10)
);
alter table APP_BASEINFORMATION
add constraint FK_BASE_INFO_PARENT foreign key (PARENTID)
references APP_BASEINFORMATIONHEADER (ID);
CREATE UNIQUE INDEX UNQ_BASE_INFO_PARENT_CODE
ON APP_BASEINFORMATION
(CASE WHEN CODE IS NOT NULL THEN PARENTID || '-' || CODE END);
as you can see above, i create UNQ_BASE_INFO_PARENT_CODE unique constraint on table APP_BASEINFORMATION when the CODE column is not null. how i can put there condition on the .hbm.xml?
these is the hibernate mapping file of APP_BASEINFORNATION:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.my.framework.model.core.baseInfo.BaseInformation" table="App_BaseInformation">
<id name="id" type="int" >
<column name="ID" />
<generator class="sequence" >
<param name="sequence">SEQ_BaseInformation</param>
</generator>
</id>
<property column="Code" name="code" not-null="false" type="integer" unique-key="??" />
<property column="Topic" name="topic" not-null="true" type="string" length="100"/>
<many-to-one entity-name="org.my.framework.model.core.baseInfo.BaseInformationHeader" name="parent" unique-key="??" >
<column name="ParentId" not-null="false" />
</many-to-one>
</class>
</hibernate-mapping>
I mean where i should put (CASE WHEN CODE IS NOT NULL THEN PARENTID || '-' || CODE END) in hbm file and what is the correct syntax?

Resources