Liquibase ignores autoIncrement="true" for Oracle DB - oracle

I generated a schema/tables via the DDL that Hibernate generated which was correct:
create table cat_component.organisation (
id number(19,0) generated as identity,
archived number(1,0),
is_in_avaloq_group number(1,0) not null,
mdm_uuid varchar2(255 char),
name varchar2(255 char),
primary key (id)
)
After that, I tried to generate Liquibase changelog which looks like this:
<changeSet author="blabla (generated)" id="1582733383680-5">
<createTable tableName="organisation">
<column autoIncrement="true" name="id" type="NUMBER(19, 0)">
<constraints primaryKey="true" primaryKeyName="organisationPK"/>
</column>
<column name="archived" type="NUMBER(1, 0)"/>
<column name="is_in_avaloq_group" type="NUMBER(1, 0)">
<constraints nullable="false"/>
</column>
<column name="mdm_uuid" type="VARCHAR2(255 CHAR)"/>
<column name="name" type="VARCHAR2(255 CHAR)"/>
</createTable>
</changeSet>
The problem is that if I try to run the liquibase changelog, it translates the XML definition into this:
2020-02-26 16:15:10.779 INFO 8064 --- [main] liquibase.executor.jvm.JdbcExecutor : CREATE TABLE CAT_COMPONENT.organisation (id NUMBER(19, 0) NOT NULL, archived NUMBER(1, 0), is_in_avaloq_group NUMBER(1, 0) NOT NULL, mdm_uuid VARCHAR2(255 CHAR), name VARCHAR2(255 CHAR), CONSTRAINT organisationPK PRIMARY KEY (id))
2020-02-26 16:15:10.787 INFO 8064 --- [main] liquibase.changelog.ChangeSet : Table organisation created
2020-02-26 16:15:10.787 INFO 8064 --- [main] liquibase.changelog.ChangeSet : ChangeSet classpath:/db/changelog/db.changelog-master.xml::1582733383680-5::blabla (generated) ran successfully in 9ms
As you may have noticed, "generated as identity" is missing, which
makes me believe that for whatever reason, the autoIncrement="true"
is ignored.
I use Oracle 12.1.0.2 and org.liquibase:liquibase-core 3.8.2.
The OJDBC driver is version 19.3.0.0.
The gradle properties are:
spring.datasource.url=jdbc:oracle:thin:#//host:1522/SID
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.datasource.username=user
spring.datasource.password=pass
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect
spring.jpa.show-sql=true
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type=trace
spring.datasource.continue-on-error=true
spring.datasource.platform=oracle
spring.liquibase.url=${spring.datasource.url}
spring.liquibase.user=${spring.datasource.username}
spring.liquibase.password=${spring.datasource.password}
spring.liquibase.url=${spring.datasource.url}
spring.liquibase.user=${spring.datasource.username}
spring.liquibase.password=${spring.datasource.password}
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.xml
spring.liquibase.default-schema=${spring.jpa.properties.hibernate.default_schema}
spring.liquibase.liquibase-schema=${spring.jpa.properties.hibernate.default_schema}
I've read the Internet, and I know that my Oracle version is compatible with this column type and I don't need to create extra oracle Sequences.
Any clue what could be the problem?

It seems to be a bug in Liquibase. I just found it here https://liquibase.jira.com/projects/CORE/issues/CORE-3524?filter=allissues&orderby=updated%20DESC&keyword=Oracle
As it states, there is a work-around to add the
generationType="BY DEFAULT"
at each column definition, where autoIncrement="true" has been used.

Correct the autoIncrement="true" is ignored due to the missing support on Oracle side for auto incremented columns as seen in the docs from liquienter link description here:
Is column an auto-increment column. Ignored on databases that do not
support autoincrement/identity functionality.
You will need to create a sequence for generating the IDs and providing the sequence to the id column when inserting the data i.e. with hibernate:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "XXX_ID_GEN")
#SequenceGenerator(name = "XXX_ID_GEN", sequenceName = "SEQ_XXX")
long id;

Related

Liquibase with column type numeric(x, y) is creating postgreSQL table with column type number

I have a spring application connected to a postgreSQL DB. My issue is that this bit of script:
<column name="utmx" type="numeric(20, 5)">
<constraints nullable="true"/>
</column>
<column name="utmy" type="numeric(20, 5)">
<constraints nullable="true"/>
</column>
Is being executed like this:
CREATE TABLE PUBLIC.table (... utmx NUMBER(20, 5), utmy NUMBER(20, 5)...
As you can see, the column type NUMERIC is being changed to NUMBER on table creation.
Why is that? How can I solve it?
Thanks in advance

Liquibase Different Datatypes H2/Oracle

So I do have the following changeset in my dbchangelog:
<changeSet id="04_04_2018" author="codeit">
<createTable tableName="BSP_TABELLE">
<column name="name" type="varchar(255)"/>
<column name="datum" type="datetime"/>
<column name="richtigfalsch" type="boolean"/>
<column name="blub" type="blob"/>
<column name="club" type="clob"/>
<column name="ganzzahl" type="int"/>
<column name="grosseganzzahl" type="bigint"/>
<column name="gleitkommazahl" type="decimal(17,5)"/>
</createTable>
</changeSet>
In H2 I do get what I expected like:
CREATE TABLE BSP_TABELLE
(
NAME VARCHAR(255),
DATUM TIMESTAMP,
RICHTIGFALSCH BOOLEAN,
BLUB BLOB,
CLUB CLOB,
GANZZAHL INTEGER,
GROSSEGANZZAHL BIGINT,
GLEITKOMMAZAHL DECIMAL(17, 5)
)
But in Oracle I do get:
CREATE TABLE BSP_TABELLE
(
NAME VARCHAR2(255),
DATUM TIMESTAMP(6),
RICHTIGFALSCH NUMBER(1),
BLUB BLOB,
CLUB CLOB,
GANZZAHL NUMBER,
GROSSEGANZZAHL NUMBER,
GLEITKOMMAZAHL NUMBER(17, 5)
)
And I expected:
CREATE TABLE BSP_TABELLE
(
NAME VARCHAR2(255),
DATUM TIMESTAMP(6),
RICHTIGFALSCH NUMBER(1),
BLUB BLOB,
CLUB CLOB,
GANZZAHL NUMBER(10),
GROSSEGANZZAHL NUMBER(19,0),
GLEITKOMMAZAHL NUMBER(17, 5)
)
How can I achieve it, that the DataType for Oracle will fit, without modifying the datatypes in H2?
I am currently loading the changelog from the java implementation at runtime like:
Connection connection = openDatabaseConnection();
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
String[] validatedLiquibaseChangelogs = validateLiquibaseChangelogs();
for (String validatedChangelog : validatedLiquibaseChangelogs) {
Liquibase liquibase = new liquibase.Liquibase(validatedChangelog, new FileSystemResourceAccessor(), database);
//Updaten der Datenbank
liquibase.update(new Contexts(), new LabelExpression());
}

Problems creating tables for Oracle on H2 in memory DB

I can't seem to figure out the right combination to get this to work. I have generated DDL from Oracle QL developer with the Quick DDL and the get_ddl functions as well as written my own SQL. All work fine in SQL Plus and SQL Developer, but I can't figure out how to get H2 to accept the SQL.
Ive tried different variations, left off the schema name, etc. Nothing seems to work.
SQL:
CREATE TABLE TEST_SCHEMA.SAVED_SEARCHES(
SEARCHID INT GENERATED ALWAYS AS IDENTITY AUTO_INCREMENT BY 1 START WITH 1 NOT NULL,
NAME VARCHAR2(30) NOT NULL,
USERID VARCHAR2(32 BYTE),
WORKGROUPID VARCHAR2(50 BYTE),
ONECLICK VARCHAR2(1 BYTE) NOT NULL
)
Connection properties:
<Context reloadable="true" crossContext="true">
<Resource
name="jdbc/cts"
auth="Container"
type="javax.sql.DataSource"
removeAbandoned="true"
removeAbandonedTimeout="30"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="test_schema"
password="oracle"
driverClassName="com.test.h2.H2Driver"
url="jdbc:testh2:mem:testdb;MODE=Oracle;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS TEST_SCHEMA\;SET SCHEMA TEST_SCHEMA"
validationQuery="SELECT 1 FROM DUAL"
/>
</Context>
Error:
Syntax error in SQL statement: expected "(, NOT, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, ,, )"; SQL statement:
Start from studying a syntax of CREATE TABLE statement in H2 database:
http://www.h2database.com/html/grammar.html#create_table
A simplified syntax is:
CREATE TABLE name (
columnDefinition,
columnDefinition,
....
);
where columnDefinition is:
http://www.h2database.com/html/grammar.html#column_definition
columnName dataType [ AUTO_INCREMENT | IDENTITY [ (startInt [,incrementInt ] ) ]]
where elements withins [] brackets are optional
As you see Oracle's syntax:
columnName dataType GENERATED ALWAYS AS IDENTITY AUTO_INCREMENT BY 1 START WITH 1
is completely different than H2's syntax:
columnName dataType IDENTITY (1,1)
You need to rewrite the CREATE TABLE statement by hand to adapt it to H2 database.
Also H2 doesn't alows for VARCHAR2 and (50 BYTE) length
you need to use just VARCHAR( 50 ) or more than 50, see this link:
http://www.h2database.com/html/datatypes.html#varchar_type

How to get Oracle next sequence value in liqiibase 1.9.5 for insert

How to get Oracle next sequence value in liqiibase 1.9.5 for insert
<changeSet author="MIGRATE" id="070214010049877">
<insert tableName="MYTABLE">
<column name="ID" valueNumeric=" nextval( 'MYTABLE_SEQ' ) "/>
<column name="NUMBER" value="5"/>
</insert>
</changeSet>
it is trying to put the value of ID " nextval( 'MYTABLE_SEQ' )" in table so it gives error
With Liquibase 1.9.5, there is no support for finding the next sequence value with the <insert> tag. You will have to either drop back to using <sql> or use <modifySql> assuming that existed in 1.9.5.

Cannot retrieve the id of the last inserted row in Hibernate using Oracle

I'm using Hibernate Tools 3.2.1.GA with the Spring version 3.0.2. I'm trying to retrieve the id of the last inserted row into the Oracle(10g) database as follows.
Session session=NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Country c=new Country();
c.setCountryId(new BigDecimal(0));
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
session.getTransaction().commit();
This statement System.out.println(c.getCountryId()); is expected to return the currently inserted id after the data is serialized to the database and before the transaction is committed but it doesn't because of the following line in the preceding code snippet (as it presumably appears to me).
c.setCountryId(new BigDecimal(0));
I'm not sure why this statement is required in my case (while inserting). I saw this statement nowhere. Omission of this line causes the following exception to be thrown.
org.hibernate.id.IdentifierGenerationException: ids for this class
must be manually assigned before calling save(): model.Country
Is this statement c.setCountryId(new BigDecimal(0)); really required during insertion? It's a sequence generated primary key in the Oracle database and because of that line, this statement System.out.println(c.getCountryId()); always returns 0 which is actually expected to return the currently inserted id in the current session.
So, how can I get the last generated id in this case? Am I following a wrong way, is there a different way?
EDIT:
CREATE TABLE "COUNTRY"
(
"COUNTRY_ID" NUMBER(35,0) NOT NULL ENABLE,
"COUNTRY_CODE" VARCHAR2(10),
"COUNTRY_NAME" VARCHAR2(50),
"ZONE_ID" NUMBER(35,0),
CONSTRAINT "COUNTRY_PK" PRIMARY KEY ("COUNTRY_ID") ENABLE,
CONSTRAINT "COUNTRY_FK" FOREIGN KEY ("ZONE_ID")
REFERENCES "ZONE" ("ZONE_ID") ON DELETE CASCADE ENABLE
)
/
CREATE OR REPLACE TRIGGER "BI_COUNTRY"
before insert on "COUNTRY"
for each row
begin
select "COUNTRY_SEQ".nextval into :NEW.COUNTRY_ID from dual;
end;
/
ALTER TRIGGER "BI_COUNTRY" ENABLE
/
The exception 'ids for this class must be manually assigned before calling save()' means that you are using the identifier generation strategy of 'Assigned'.
assigned
lets the application assign an identifier to the object before save() is called. This is the default strategy if no element is specified.
If you do not define any strategy, hibernate defaults to 'assigned'. 'assigned' strategy implies that hibernate expects that the application supplies it's own ids.
If you want to use a sequence id generator in Oracle, you can do so with the following configuration -
If you are using xml -
<id name="countryId" type="java.lang.Integer">
<column name="Country_Id" />
<generator class="sequence">
<param name="sequence">Country_Id_Seq</param>
</generator>
</id>
If you are using annotations -
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="Country_Id_Seq")
#SequenceGenerator(name="Country_Id_Seq", sequenceName="Country_Id_Seq" )
private Integer sequence;
And your code should look like so -
Country c=new Country();
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
When 'session.save(c)' executes, hibernate makes the following sql call to Oracle, retrieves the id and sets it in Country object.
select Country_Id_Seq.nextVal from dual;
Problem with trigger
Since you are using a trigger to increment the id when a row is inserted, this will cause a problem with hibernate sequence. Hibernate is using the sequence to generate an id and the database is using the trigger to increment the id. This is resulting in the id being incremented twice.
You have a three options to resolve this.
Delete the trigger because it's not necessary.
If you still need the trigger because the table could be updated outside the application, you could update the trigger such that the id is generated only if the id is not set in the insert statement
HIbernate issue with Oracle Trigger for generating id from a sequence
Create a custom id generator that uses the trigger to set the id in the data before it is saved to db. Check out the following link - https://forum.hibernate.org/viewtopic.php?t=973262
If the values into an ID column generated by a sequence, then you should associate that sequence with your ID column in the entity definition so that the attribute is filled in with the ID value by Hibernate during insertion.
Using annotations:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CountryIdSequence")
#SequenceGenerator(name = "CountryIdSequence", sequenceName = "COUNTRY_ID_SEQUENCE")
#Column(name = "COUNTRY_ID")
private BigDecimal countryId;
Using hbm:
<id name="countryId" type="big_decimal">
<column name="COUNTRY_ID" />
<generator class=""sequence">
<param name="sequence">COUNTRY_ID_SEQUENCE</param>
</generator>
</id>
Then, it will be available after the save.
Any changes made to the entity at the database layer are not reflected in the hibernate entity layer until you refresh the object.
session.save(c);
session.flush();
// Refresh the object for columns modified in the DB by IDENTITY / SEQUENCE / Triggers.
session.refresh(c);
System.out.println(c.getCountryId());

Resources