H2: Error with a multi-row insert statement - java

I'm using an in-memory H2 database with an initialisation SQL script. The following table is created:
CREATE TABLE GINA_T_WAM_GUESTDOMAIN (
WFD_C_NAME VARCHAR(12) NOT NULL,
WFD_C_BASEURI VARCHAR(128) NOT NULL,
PRIMARY KEY (WFD_C_NAME, WFD_C_BASEURI),
FOREIGN KEY (WFD_C_NAME) REFERENCES GINA_T_WAM_GUEST(WFG_C_NAME)
);
Now I can insert some rows in that table like this:
INSERT INTO GINA_T_WAM_GUESTDOMAIN(WFD_C_NAME,WFD_C_BASEURI)
VALUES('DEVINT', 'https://dev.etat-ge.ch');
INSERT INTO GINA_T_WAM_GUESTDOMAIN(WFD_C_NAME,WFD_C_BASEURI)
VALUES('DEVINT', 'https://devtech.etat-ge.ch');
But when I try to insert both rows in a single statement:
INSERT INTO GINA_T_WAM_GUESTDOMAIN(WFD_C_NAME,WFD_C_BASEURI)
VALUES('DEVINT', 'https://dev.etat-ge.ch'),
VALUES('DEVINT', 'https://devtech.etat-ge.ch');
I get the following error message:
Column count does not match
Does anyone knows about this issue? is it at all possible to insert several lines in one statement?

That's how you can insert multiple rows at the same time in a single SQL statement:
VALUES('DEVINT', 'https://dev.etat-ge.ch'),
('DEVINT', 'https://devtech.etat-ge.ch');
It's called row value constructor and is a standard since SQL-92.
Source: http://www.andrew.cmu.edu/user/shadow/sql/sql1992.txt (Chapter 7.1)

Related

JOOQ store() does not return ResultSet

Following this I try to create a new Student Record, assign it some Values and store it to the Database.
DSLContext ctx = ...
StudentRecord s = ctx.newRecord(Student.STUDENT);
s.setFirstname("Nicolas");
s.setLastname("Nox");
s.setGender("M");
s.setYearOfBirth((short) 1990);
s.store(); <-- ERROR
EDIT 1:
The same goes for insert()
End of Edit 1
EDIT 2:
The Student Table contains nothing more than the four values shown above. The Model is autogenerated and select worked so far.
End of Edit 2
Edit 3:
As asked here the Table Definition for Student and the dependency of the JDBC Driver.
create table [LECTURE_DB].[dbo].[STUDENT](
[ID] int identity(1, 1) not null,
[FIRSTNAME] nvarchar(20) not null,
[LASTNAME] nvarchar(20) not null,
[YEAR_OF_BIRTH] smallint null,
[GENDER] nvarchar(1) null,
constraint [PK__STUDENT__3214EC277F60ED59]
primary key ([ID]),
constraint [STUDENT_NAME_IDX]
unique (
[LASTNAME],
[FIRSTNAME]
)
)
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>7.0.0.jre8</version>
</dependency>
End of Edit 3
This does insert a Value into the Database but the Record is not correctly updated.
The following Error is thrown:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: org.jooq.exception.DataAccessException: SQL [declare #result table ([ID] int); insert
into [LECTURE_DB].[dbo].[STUDENT] ([FIRSTNAME], [LASTNAME], [YEAR_OF_BIRTH], [GENDER]) output
[inserted].[ID] into #result values (?, ?, ?, ?); select [r].[ID] from #result [r];]; The statement did not return a result set.
at org.jooq_3.12.1.SQLSERVER2014.debug(Unknown Source)
at org.jooq.impl.Tools.translate(Tools.java:2717)
at org.jooq.impl.DefaultExecuteContext.sqlException(DefaultExecuteContext.java:755)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:383)
at org.jooq.impl.TableRecordImpl.storeInsert0(TableRecordImpl.java:206)
at org.jooq.impl.TableRecordImpl$1.operate(TableRecordImpl.java:177)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:130)
at org.jooq.impl.TableRecordImpl.storeInsert(TableRecordImpl.java:173)
at org.jooq.impl.UpdatableRecordImpl.store0(UpdatableRecordImpl.java:196)
at org.jooq.impl.UpdatableRecordImpl$1.operate(UpdatableRecordImpl.java:136)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:130)
at org.jooq.impl.UpdatableRecordImpl.store(UpdatableRecordImpl.java:132)
at org.jooq.impl.UpdatableRecordImpl.store(UpdatableRecordImpl.java:124)
at de.esteam.lecturedb.jooq.LectureDBSetup.insertInitialData(LectureDBSetup.java:49)
at de.esteam.lecturedb.jooq.LectureDBAnalysis.<init>(LectureDBAnalysis.java:77)
at de.esteam.lecturedb.jooq.LectureDBAnalysis.<clinit>(LectureDBAnalysis.java:44)
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The statement did not return a result set. at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:206)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:464)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:405)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7535)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2438)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:208)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:183)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeQuery(SQLServerPreparedStatement.java:317)
at org.jooq.tools.jdbc.DefaultPreparedStatement.executeQuery(DefaultPreparedStatement.java:94)
at org.jooq.impl.AbstractDMLQuery.executeReturningQuery(AbstractDMLQuery.java:1137)
at org.jooq.impl.AbstractDMLQuery.execute(AbstractDMLQuery.java:935)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:369)
... 12 more
stating the missing ResultSet from the Database to update the ID in my Application.
I kinda need the Records and the IDs since I need to insert many values with foreign keys.
jOOQ using SQL Server OUTPUT to fetch result data from DML statements is a new feature from jOOQ 3.12: #4498. It has a few known issues in version 3.12.2, including:
https://github.com/jOOQ/jOOQ/issues/8917
Yours was not known yet. I'll update my answer once I know more about it.
A workaround could be to turn off generating the OUTPUT clause in SQL Server, which should still work for single-row DML statements like yours. Set your Settings.renderOutputForSQLServerReturningClause flag to false

Java: how to perform batch insert with identity column using java jdbc for Sql Server

I have a csv file which I need to write to a Sql Server table using SQLServerBulkCopy. I am using SQLServerBulkCSVFileRecord to load data from the file.
The target table has the following structure:
create table TEST
(
ID int identity,
FIELD_1 int,
FIELD_2 varchar(20)
)
The csv file has the following structure:
4279895;AA00000002D
4279895;AA00000002D
4279895;AA00000002D
4279896;AA00000003E
4279896;AA00000003E
4279896;AA00000003E
As you can see the ID (identity) column is not present in the csv, I need the database to automatically add the identity value on insert.
My problem is that the bulk insert does not work as long as the table has the identity column, I got the following error:
com.microsoft.sqlserver.jdbc.SQLServerException: Source and destination schemas do not match.
at com.microsoft.sqlserver.jdbc.SQLServerBulkCopy.validateColumnMappings(SQLServerBulkCopy.java:1749)
at com.microsoft.sqlserver.jdbc.SQLServerBulkCopy.writeToServer(SQLServerBulkCopy.java:1579)
at com.microsoft.sqlserver.jdbc.SQLServerBulkCopy.writeToServer(SQLServerBulkCopy.java:606)
This is the relevant code:
try (
Connection targetConnection = DriverManager.getConnection(Configuration.TARGET_CONNECTION_URL);
SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(targetConnection);
SQLServerBulkCSVFileRecord fileRecord = new SQLServerBulkCSVFileRecord(csvPath, Charsets.UTF_8.toString(), ";", false);
) {
SQLServerBulkCopyOptions copyOptions = new SQLServerBulkCopyOptions();
copyOptions.setKeepIdentity(false);
bulkCopy.setBulkCopyOptions(copyOptions);
fileRecord.addColumnMetadata(1, null, java.sql.Types.INTEGER, 0, 0); // FIELD_1 int
fileRecord.addColumnMetadata(2, null, java.sql.Types.VARCHAR, 20, 0); // FIELD_2 varchar(20)
bulkCopy.setDestinationTableName("TEST");
bulkCopy.writeToServer(fileRecord);
}
catch (Exception e) {
// [...]
}
The bulk insert ends succesfully if I remove the identity column from the table. Which is the correct to perform an identity bulk-insert using java jdbc for Sql Server?
I think you don't need to set this option copyOptions.setKeepIdentity(false);
Try after removing this line. You can refer to this post as well SqlBulkCopy Insert with Identity Column
If you have a leading column with blank values Helen the identity will be generated on insert. Depending on the settings it might generate new identity even if the first column is not blank.
So either add an extra column or use another (staging) table.
BTW, if you have a really big table the command-line bcp utility is the fastest. From experience up to 5 times faster compared to Jdbc batch insert.

How to count rows affected by cascade

Let's say I have two tables A and B.
Table B has a foreign key that references Table A's primary key.
And the foreign key has 'on delete cascade' constraint.
And I am also using Java and JDBC to access this database.
How do I know the number of rows of Table B affected by delete cascade constraint?
I have to get the number in Java Application.
Does connection object or something has any parameter or method that returns it?
To follow after DELETE FROM statement you can use an audit table and BEFORE DELETE trigger.
// change type of deleted_id to your primary key's type.
create table deleted_audit ( deleted_id BIGINT, dt TIMESTAMP, count INT );
DELIMITER $$
CREATE TRIGGER count_deleted BEFORE delete ON a
FOR EACH ROW
BEGIN
DECLARE count_items INT;
SELECT count(*) FROM b WHERE a_id=OLD.id INTO count_items;
INSERT INTO deleted_audit VALUES( OLD.id, NOW(), count_items);
END; $$
DELIMITER ;

Insert Returning Query For MySQL in JOOQ

am trying to use the following code to get the auto generated id . My back end is MySQL. Code looks like this
Record record = create.insertInto(CANDIDATE, CANDIDATE.FIRST_NAME,
CANDIDATE.LAST_NAME,CANDIDATE.EXTRACTED_NAME)
.values("Charlotte", "Roche","Charlotte Roche")
.returning(CANDIDATE.ID)
.fetchOne();
System.out.println(record.getValue(CANDIDATE.ID));
I am getting NullPointerException. I took a look at http://www.jooq.org/javadoc/latest/org/jooq/InsertReturningStep.html .
It says
Derby, H2, Ingres, MySQL, SQL Server only allow for retrieving IDENTITY column values as "generated key". If other fields are requested, a second statement is issued. Client code must assure transactional integrity between the two statements.
As per my understanding in Mysql auto_increment works as IDENTITY. Can anybody please throw some light on how to achieve this for MySQL
I have taken a look at this SO Question on a similar topic and tried following
Result<?> record =
create.insertInto(CANDIDATE, CANDIDATE.FIRST_NAME, CANDIDATE.LAST_NAME,CANDIDATE.EXTRACTED_NAME)
.values("Charlotte", "Roche","Charlotte Roche")
.returning(CANDIDATE.ID)
.fetch();
System.out.println(record.size());
Though it inserts record in the backend but it prints the record.size() as zero
I'm know that I'm late for the party.
But I hope I can help someone with similar problem,
Derby, H2, Ingres, MySQL, SQL Server only allow for retrieving IDENTITY column values as "generated key". If other fields are requested, a second statement is issued. Client code must assure transactional integrity between the two statements.
The words "generated key" is the problem.
You can check if your table id is AUTO_INCREMENT or not by using SHOW CREATE TABLE $table_name. I think it is not.
P/s: I'm using MySQL
Just did a test inserting a record and retrieving the generated id from within a Spring service without any problem.
So yes, auto_increment in MySQL works as IDENTITY with jOOQ.
The MySQL table looks like this:
CREATE TABLE persons (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`first_name` varchar(64) NOT NULL,
`last_name` varchar(64) NOT NULL,
primary key(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and the service like this:
public Result<PersonsRecord> insertPerson(String firstName, String lastName) {
Result<PersonsRecord> result =
dsl
.insertInto(
PERSONS,
PERSONS.FIRST_NAME,
PERSONS.LAST_NAME)
.values(
firstName,
lastName)
.returning(PERSONS.ID)
.fetch();
logger.debug("Person ID: " + result.getValue(0, PERSONS.ID));
return result;
}
The generated id is available straight away after executing the insert:
Person ID: 4
Maybe there is a problem with transaction.
Insert might not yet persisted those values in database, so nothing is fetched.
Also I think that IDENTITY in case of MySQL is not made by AUTO_INCREMENT but PRIMARY KEY (...)
https://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

How can I get an id for a new record in a generic way? (JOOQ 3.4 with Postgres)

With jooq 3.4 I can't figure out how to do this (with Postgresql):
Query query = dsl.insertInto(TABLE)
.set(TABLE.ID, Sequences.TABLE_ID_SEQ.nextval());
but in a case when I don't know which is the exact table, something like this:
TableImpl<?> tableImpl;
Query query = dsl.insertInto(tableImpl)
.set(tableImpl.getIdentity(), tableImpl.getIdentity().getSequence().nextval());
Is it somehow possible?
I tried this:
dsl.insertInto(tableImpl)
.set(DSL.field("id"),
tableImpl.getSchema().getSequence("table_id_seq").nextval())
This works but I still don't know how to get the sequence name from the TableImpl object.
Is there a solution for this? Or is there a problem with my approach?
In plain SQL I would do this:
insert into table_A (id) VALUES nextval('table_A_id_seq');
insert into table_B (table_A_id, some_val) VALUES (currval('table_A_id_seq'), some_val);
So I need the value or a reference to that id for later use of the id that was generated for the inserted record as default, but I don't want to set any other values.
jOOQ currently doesn't have any means of associating a table with its implicitly used sequence for the identity column. The reason for this is that the sequence is generated when the table is created, but it isn't formally connected to that table.
Usually, you don't have to explicitly set the serial value of a column in a PostgreSQL database. It is generated automatically on insert. In terms of DDL, this means:
CREATE TABLE tablename (
colname SERIAL
);
is equivalent to specifying:
CREATE SEQUENCE tablename_colname_seq;
CREATE TABLE tablename (
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
The above is taken from:
http://www.postgresql.org/docs/9.3/static/datatype-numeric.html#DATATYPE-SERIAL
In other words, just leave out the ID values from the INSERT statements.
"Empty" INSERT statements
Note that if you want to create an "empty" INSERT statement, i.e. a statement where you pass no values at all, generating a new column with a generated ID, you can use the DEFAULT VALUES clause.
With SQL
INSERT INTO tablename DEFAULT VALUES
With jOOQ
DSL.using(configuration)
.insertInto(TABLENAME)
.defaultValues()
.execute();
Returning IDs
Note that PostgreSQL has native support for an INSERT .. RETURNING clause, which is also supported by jOOQ:
With SQL
INSERT INTO tablename (...) VALUES (...) RETURNING ID
With jOOQ
DSL.using(configuration)
.insertInto(TABLENAME, ...)
.values(...)
.returning(TABLENAME.ID)
.fetchOne();

Categories