Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I am wondering on how to generate scripts (DDL, DML) from an specific template to different databases, like Oracle, MSSQL, Sybase.
Liquibase seems to fit, but is there any framework or way to do it?
The following example generates SQL for the H2 database. The XML changeset enables liquibase to generate database specific SQL.
Example
Install the jars
mkdir lib
curl http://search.maven.org/remotecontent?filepath=org/liquibase/liquibase-core/3.0.8/liquibase-core-3.0.8.jar -o lib/liquibase.jar
curl http://search.maven.org/remotecontent?filepath=com/h2database/h2/1.3.174/h2-1.3.174.jar -o lib/h2.jar
This is a cheat. You could also download and install the zip package.
Sample changelog file
This changelog creates a single table. The following format is XML, other supported formats are YAML and SQL.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet author="mark" id="1">
<createTable tableName="EMPLOYEE">
<column name="EMPNO" type="INT">
<constraints nullable="false" primaryKey="true" primaryKeyName="EMP_PK"/>
</column>
<column name="NAME" type="VARCHAR(10)"/>
<column name="JOB" type="VARCHAR(9)"/>
<column name="BOSS" type="INT"/>
<column name="HIREDATE" type="VARCHAR(12)"/>
<column name="SALARY" type="DECIMAL(7,2)"/>
<column name="COMM" type="DECIMAL(7,2)"/>
<column name="DEPTNO" type="INT"/>
</createTable>
</changeSet>
</databaseChangeLog>
Running
Liquibase can be run as follows to generate the SQL
java -jar ./lib/liquibase.jar \
--classpath=lib/h2.jar \
--url=jdbc:h2:db/scottTiger \
--driver=org.h2.Driver \
--username=user \
--password=pass \
--changeLogFile=sample.xml \
updateSQL
The "update" command would apply the SQL against the database.
Generated output
-- *********************************************************************
-- Update Database Script
-- *********************************************************************
-- Change Log: sample.xml
-- Ran at: 22/12/13 22:30
-- Against: USER#jdbc:h2:db/scottTiger
-- Liquibase version: 3.0.8
-- *********************************************************************
-- Create Database Lock Table
CREATE TABLE PUBLIC.DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID));
-- Initialize Database Lock Table
DELETE FROM PUBLIC.DATABASECHANGELOGLOCK;
INSERT INTO PUBLIC.DATABASECHANGELOGLOCK (ID, LOCKED) VALUES (1, FALSE);
-- Lock Database
-- Create Database Change Log Table
CREATE TABLE PUBLIC.DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED TIMESTAMP NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35), DESCRIPTION VARCHAR(255), COMMENTS VARCHAR(255), TAG VARCHAR(255), LIQUIBASE VARCHAR(20));
-- Changeset sample.xml::1::mark
CREATE TABLE PUBLIC.EMPLOYEE (EMPNO INT NOT NULL, NAME VARCHAR(10), JOB VARCHAR(9), BOSS INT, HIREDATE VARCHAR(12), SALARY DECIMAL(7, 2), COMM DECIMAL(7, 2), DEPTNO INT, CONSTRAINT EMP_PK PRIMARY KEY (EMPNO));
INSERT INTO PUBLIC.DATABASECHANGELOG (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, DESCRIPTION, COMMENTS, EXECTYPE, LIQUIBASE) VALUES ('1', 'mark', 'sample.xml', NOW(), 1, '7:4700326f252366e9cfe598fded5037c8', 'createTable', '', 'EXECUTED', '3.0.8');
Liquibase creates a special table called "DATABASECHANGELOG" to track all changes. Look carefully and you'll find the table create statement for the "EMPLOYEE" table.
This example if for H2. Other databases are supported.
Related
Using the following changeset in liquibase to create a table with a foreign key is possible and works.
<changeSet author="cibn" context="initialSchema" id="initialSchema-edited-1.0.4">
<createTable tableName="prices">
<column name="articleId" type="String">
<constraints nullable="false" foreignKeyName="fk_articles_articleId" references="articles(articleId)"/>
</column>
...
</changeSet>
However, the addForeignKeyConstraint change after creation of the initial schema is not supported.
https://www.liquibase.org/documentation/changes/add_foreign_key_constraint.html
Why? and could this be changed?
I believe that's because ADD CONSTRAINT is not supported by SQLite for ALTER TABLE feature, and that's exactly what Liquibase does during addForeignKeyConstraint change.
Here's the documentation SQL Features That SQLite Does Not Implement
Only the RENAME TABLE, ADD COLUMN, and RENAME COLUMN variants of the ALTER TABLE command are supported. Other kinds of ALTER TABLE operations such as DROP COLUMN, ALTER COLUMN, ADD CONSTRAINT, and so forth are omitted.
How do i set the autoincrement property using 'startWith' on a column in PostgreSQL using liquibase??
For some reason it always starts from 1. I tried using a custom sequence but that didn't help either.
<column autoIncrement="true" startWith="100" name="id" type="bigint">
That's my current column definition which does not work.
EDIT:
I want to import data from csv using liquibase. I tried the following:
<changeSet author="author" id="createSequence">
<createSequence
incrementBy="1"
sequenceName="mytable_id_seq"
startValue="1000"/>
</changeSet>
</changeSet>
<changeSet author="author" id="1-mytable">
<createTable tableName="mytable">
<column name="id" type="BIGSERIAL" defaultValueComputed="nextval('mytable_id_seq')">
<constraints primaryKey="true" primaryKeyName="mytable_pkey"/>
</column>
</createTable>
<loadData encoding="UTF-8"
file="liquibase/data/mytable.csv"
separator=","
tableName="mytable">
</loadData>
</changeSet>
If i try this I receive the following error 'currval of sequence "table_id_seq" is not yet defined in this session' and I think that it uses the sequence from the public schema instead of what i have set to liquibase.
Another thing i tried was to update it manually:
ALTER SEQUENCE mytable_id_seq restart with 100;
In this case the sequence used was the one from the public schema, but i want to use the schema set to liquibase
Instead of using bigserial which is an autoincrementing bigint specific to postgres use bigint if you are going to be setting up your own increment and sequence.
"The data types smallserial, serial and bigserial are not true types, but merely a notational convenience for creating unique identifier columns (similar to the AUTO_INCREMENT property supported by some other databases). In the current implementation, specifying:"
CREATE TABLE tablename (
colname SERIAL
);
is the same as
CREATE SEQUENCE tablename_colname_seq AS integer;
CREATE TABLE tablename (
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
From here
https://www.postgresql.org/docs/12/datatype-numeric.html
I am working on a database application which is mostly read-only, but there is one table which records user movement in the app and has a large number of writes to it. For every few thousand writes, we see a few exceptions in the error log like so:
[WARN][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] SQL Error: 1062, SQLState: 23000
[ERROR][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] Duplicate entry '17011' for key 1
[ERROR][2009-07-30 11:09:20,083][org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
The table in question has the following schema:
CREATE TABLE IF NOT EXISTS `my_table` (
`id` int(11) NOT NULL,
`data1` int(11) NOT NULL,
`data2` int(11) NOT NULL,
`timestamp` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
And the corresponding Hibernate mapping XML:
<hibernate-mapping>
<class name="mycorp.MyClass" table="my_table">
<id name="id" column="id" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="data1" column="data1" type="java.lang.Integer"/>
<property name="data2" column="data2" type="java.lang.Integer"/>
<property name="timestamp" column="timestamp" type="java.util.Date"/>
</class>
</hibernate-mapping>
It is possible, though unlikely, that multiple instances of our webapp could be writing to the database at once, since we version numbers in our webapp context to seamlessly release new versions of the applications. Clients with the old version of the application cached in their web browser would thus access the old versions of the server, which we undeploy after a few weeks.
Anyways, I'm not convinced that this is the problem, but I'm suspicious that there is some synchronization issue between MySQL and Hibernate at hand here. Would changing my generator to sequence, seqhilo or hilo help? Also, if you can provide an example of setting up such a generator in MySQL, that would be very helpful, as most of the resources online are simply copy-pasted from the woefully minimalistic examples in the Hibernate manual.
Increment is definitely bad if you have more than one process writing to the same table - you're bound to have collisions.
Since it is MySQL we're talking about, the easiest thing to use would be identity. In your Hibernate mapping:
<generator class="identity"/>
In your MySQL script:
CREATE TABLE IF NOT EXISTS `my_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`data1` int(11) NOT NULL,
`data2` int(11) NOT NULL,
`timestamp` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
To alter an existing table:
ALTER TABLE `my_table`
CHANGE COLUMN `id` `id` int(11) NOT NULL AUTO_INCREMENT=$NEW_VALUE$;
where $NEW_VALUE$ should be replaced by the next available id so that sequence does not reset to 1.
I have a Java WebApplication which uses Spring and Hibernate to create and populate a table. The table has a unique id which is set by the following Hibernate configuration:
<hibernate-mapping>
<class name="com.myco.TransactionImpl" table="myco_transaction">
<id name="id" type="java.lang.Integer">
<generator class="native">
<param name="sequence">id_column_sequence</param>
</generator>
</id>
<property name="user" type="java.lang.String">
<column name="tx_user" not-null="false" unique="false" />
</property>
...
</class>
</hibernate-mapping>
This works fine for all of our MySQL users. However one users who is using SQLServer 2008 Developer Edition seems to find that whenever the application attempts to put a value into this table, the system throws an error of the type:
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 339, SQLState: S0001
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - DEFAULT or NULL are not allowed as explicit identity values.
ERROR Exception - Error attempting to save instance:
This error seems quite clear. The id has not been generated. But why?
We have tried to resolve this issue in-house using SQL Server 2014 and SQL Server Express 2008 however when we use either of those systems, the insert into the table works and the ids are automatically generated.
I am at a loss as to how to proceed. I believe that the problem is one of the following:
The user has somehow modified his database to not create sequences. If this is possible how do I go about reproducing this or asking the user how to check?
There is something fundamentally different about SQL Server 2008 Express and SQL Server 2008 Developer Edition.
The hibernate generation strategy is wrong for SQL Server 2008.
Any help or advice on how to proceed with this issue greatly appreciated.
Update :
Table Definition on the SQL Server 2008 machine:
CREATE TABLE [dbo].[myco_transaction](
[id] [int] IDENTITY(1,1) NOT NULL,
[tx_user] [varchar](255) NULL,
[tx_date] [numeric](19, 0) NOT NULL,
[restored_tx] [int] NULL,
[restored_maint_urn] [varchar](255) NULL,
[ds_action] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Update:
The user has stated this also fails on his version of SQL Server 2012 Developer Edition.
Since the identity is already defined on the table and working, you don't need to insert it. The table will automatically generate it upon insertion. If you are wanting to insert your own identity, you will need to run an IDENTITY_INSERT ON command before doing the insert. A word of warning, the insert will error if you are trying to insert an already generated identity. This type of operation is usually done on a fresh and empty table.
I have one sqlserver 2008 r2 datatable, it has one column autoId int identity(1,1), but it's not the primary key, another column varchar(20) is the one.
question is : how do i config the hbm file?
bellow is my config file,but it got errors when i try to save one instance.
"Cannot insert explicit value for identity column in table 'acct_info' when IDENTITY_INSERT is set to OFF."
<property name="autoId" type="int">
<column name="auto_id" not-null="true" unique="true" />
</property>
There can be two reasons , either you don't have sufficient privileges in DB for IDENTITY INSERT or there is mismatch in the mechanism by which you are trying to set an identifier in hibernate and DB layer.
You can have a look at your id generation strategy in hibernate definition file
In DB you can change to Set IDENTITY_INSERT to "ON"
Pick a different generator class