org.h2.jdbc.JdbcSQLSyntaxErrorException after H2 version upgrade - java

I recently upgraded h2 version from 1.4.200 to 2.0.206. Some of the queries that used to work in the older version are not working properly after the upgrade.
CREATE TABLE SOMETABLE (
ID INT(11) NOT NULL AUTO_INCREMENT,
SOURCE_ID VARCHAR(255) NOT NULL,
MESSAGE VARCHAR(255) NOT NULL,
PRIMARY KEY (`ID`)
);
CREATE TABLE IF NOT EXISTS SOMEOTHERTABLE (
ID VARCHAR(255) NOT NULL,
NAME VARCHAR(255) NOT NULL,
CREATED_TIME TIMESTAMP NOT NULL,
LAST_MODIFIED TIMESTAMP NOT NULL,
HAS_FILE BOOLEAN(1) NOT NULL,
PRIMARY KEY (ID)
);
For both these, I get similar errors
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement " CREATE TABLE SOMETABLE ( ID INT([*]11) NOT NULL AUTO_INCREMENT, SOURCE_ID VARCHAR(255) NOT NULL, MESSAGE VARCHAR(255) NOT NULL, PRIMARY KEY (`ID`) )"; expected "ARRAY, INVISIBLE, VISIBLE, NOT, NULL, AS, DEFAULT, GENERATED, ON, NOT, NULL, AUTO_INCREMENT, DEFAULT, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, COMMENT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, AUTO_INCREMENT, ., )";
org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement " CREATE TABLE IF NOT EXISTS SOMEOTHERTABLE ( ID VARCHAR(255) NOT NULL, NAME VARCHAR(255) NOT NULL, CREATED_TIME TIMESTAMP NOT NULL, LAST_MODIFIED TIMESTAMP NOT NULL, HAS_FILE BOOLEAN([*]1) NOT NULL, PRIMARY KEY (ID) )"; expected "ARRAY, INVISIBLE, VISIBLE, NOT, NULL, AS, DEFAULT, GENERATED, ON, NOT, NULL, AUTO_INCREMENT, DEFAULT, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, COMMENT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, AUTO_INCREMENT, ., )";
It seems that in both these cases, having INT(11) and BOOLEAN(1) is the issue. Are those not allowed anymore in the new version? If so, how should I change those? Any help regarding this is appreciated.

I was facing the same issue when updating the h2 version from 1.4.200 to 2.0.206. The project is based on Spring Boot and uses Hibernate.
In my case the problem was, that i had an entity with a field referencing a table column called "VALUE".
#Column(name = "VALUE")
private BigDecimal value;
According to the docs https://h2database.com/html/advanced.html#keywords, VALUE is a reserved keyword. Changing the column reference "VALUE" to something like "VALUE1" solved the issue.

Why do you have such definitions? Documentation of H2 1.4.200 doesn't allow any parameters for these data types.
INT(11) is allowed only in MySQL and MariaDB compatibility modes, but the specified precision is ignored by H2. This definition is rejected in all other compatibility modes in H2 2.0, you need to use INT or INTEGER.
BOOLEAN(1) is not allowed at all, if it worked in 1.4.200, it was a bug in the parser. You need to use BOOLEAN.
AUTO_INCREMENT clause also should normally be used only in MySQL and MariaDB compatibility modes, but it works in Regular mode too. The proper clause is GENERATED BY DEFAULT AS IDENTITY and explicit NOT NULL constraint isn't required for primary key and identity columns, you can remove it. Constraints also should normally be specified after all other clauses, NOT NULL before identity options is actually accepted by H2, but this wrong order of clauses isn't documented and isn't supported.

Had a similar issue with Hibernate and Spring Boot and as #Plumstone mentioned it was due to a reserved keyword (in my case constraint). Adding backticks to the name, solved the issue:
#Column(name = "`constraint`")
private String constraint;

Related

Trouble inserting foreign key with H2

I'm using H2 to test my database code. To do that, I start by reading in a set of SQL files and using RunScript.execute to execute the SQL.
My real database is MySQL, and I have tested the SQL scripts to make sure they work and they do. The problem is decidedly inside of H2. Here's the set of scripts:
CREATE TABLE stores (
id integer AUTO_INCREMENT PRIMARY KEY,
name varchar(255) NOT NULL,
UNIQUE(name),
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp
);
CREATE TABLE regions (
id integer AUTO_INCREMENT PRIMARY KEY,
name varchar(255) NOT NULL,
UNIQUE(name),
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp
);
CREATE TABLE products (
id integer AUTO_INCREMENT PRIMARY KEY,
store_id integer NOT NULL,
name varchar(255) NOT NULL,
region_id integer NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp,
UNIQUE(store_id, name),
FOREIGN KEY store_fk(store_id) REFERENCES stores(id),
FOREIGN KEY region_fk(region_id) REFERENCES regions(id)
);
Running these 3 scripts to create the tables using MySQL Workbench works fine. Running them from JUnit via the follow code does not:
Reader storeTableSql = new InputStreamReader(
DatabaseConnectorTests.class.getResourceAsStream(
"/migrations/V1__create_stores_table.sql"));
Reader regionTableSql = new InputStreamReader(
DatabaseConnectorTests.class.getResourceAsStream(
"/migrations/V2__create_regions_table.sql"));
Reader productTableSql = new InputStreamReader(
DatabaseConnectorTests.class.getResourceAsStream(
"/migrations/V3__create_products_table.sql"));
this.dc = DatabaseConnector.getConnection();
RunScript.execute(dc, storeTableSql);
RunScript.execute(dc, regionTableSql);
RunScript.execute(dc, productTableSql);
I get the following error:
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "CREATE TABLE PRODUCTS (
ID INTEGER AUTO_INCREMENT PRIMARY KEY,
STORE_ID INTEGER NOT NULL,
NAME VARCHAR(255) NOT NULL,
REGION_ID INTEGER NOT NULL,
CREATED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UPDATED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
DELETED_AT TIMESTAMP,
UNIQUE(STORE_ID, NAME),
FOREIGN KEY STORE_FK[*](STORE_ID) REFERENCES STORES(ID),
FOREIGN KEY REGION_FK(REGION_ID) REFERENCES REGIONS(ID)
) "; expected "("; SQL statement:
CREATE TABLE products (
id integer AUTO_INCREMENT PRIMARY KEY,
store_id integer NOT NULL,
name varchar(255) NOT NULL,
region_id integer NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp,
UNIQUE(store_id, name),
FOREIGN KEY store_fk(store_id) REFERENCES stores(id),
FOREIGN KEY region_fk(region_id) REFERENCES regions(id)
) [42001-196]
This code works in MySQL, and I am running H2 in MySQL compatibility mode via the following connection string: jdbc:h2:file:~/database;MODE=MySQL. I'm not sure what else I can do here. This error seems very strange to me, as I have no idea why it would think I need ANOTHER (.
Does anyone know how to fix this? Or have a better suggestion on how to seed my test database? I cannot use hibernate for this task due to a restriction on the system I am running on so I'm forced to do a lot manually.
Thank you!
From this post, but with a little more explanation.
Foreign key constraints in H2 do not allowed named constraints for whatever reason. The solution to this problem is to simply remove the name:
CREATE TABLE products (
id integer AUTO_INCREMENT PRIMARY KEY,
store_id integer NOT NULL,
name varchar(255) NOT NULL,
region_id integer NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp,
UNIQUE(store_id, name),
FOREIGN KEY (store_id) REFERENCES stores(id),
FOREIGN KEY (region_id) REFERENCES regions(id)
);
The error message is so unclear and misleading. Hopefully this helps someone.

Already using utf8mb4 but getting 1366: Incorrect string value: '\xF0\x9F\x98\x81\xF0\x9F...'

All answers to the issue "Incorrect string value" point to using utf8mb4. However, I'm still using that I still get that error.
Running
SELECT CCSA.character_set_name, collation_name FROM information_schema.`TABLES` T,
information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
WHERE CCSA.collation_name = T.table_collation
AND T.table_schema = "test"
AND T.table_name = "products";
produces
character_set_name collation_name
utf8mb4 utf8mb4_unicode_ci
And running
SELECT character_set_name, collation_name FROM information_schema.`COLUMNS`
WHERE table_schema = "test"
AND table_name = "products"
AND column_name = "description";
produces the same result. Then, when I try to update the column description:
UPDATE test.products SET description='Esperienza molto negativa... 😁😁😁😁SCONSIGLIATISSIMO' WHERE id='50'
I get the abovementioned error
1366: Incorrect string value: '\xF0\x9F\x98\x81\xF0\x9F...' for column 'description' at row 1
I've tried this update both through a jdbc connection jdbc:mysql://localhost:3306/test?useUnicode=yes;characterEncoding=utf8; and directly through MySQLWorkbench. I don't know what else to try of look for. What am I missing?
EDIT
Output of SHOW CREATE TABLE
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`manufacturerId_fk` int(11) NOT NULL,
`type_fk` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'TEST',
`ownerId_fk` int(11) DEFAULT NULL,
`rating_fk` int(11) NOT NULL,
`description` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`authorName` varchar(75) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`authorUrl` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `manufacturer_authorUrl_UNIQUE` (`manufacturerId_fk`,`authorUrl`),
KEY `fk_products_manufacturers_idx` (`manufacturerId_fk`),
KEY `fk_products_users_idx` (`ownerId_fk`),
KEY `fk_products_validRatings_idx` (`rating_fk`),
KEY `fk_products_productTypes` (`type_fk`),
CONSTRAINT `fk_products_productTypes` FOREIGN KEY (`type_fk`) REFERENCES `productTypes` (`type`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_products_manufacturers` FOREIGN KEY (`manufacturerId_fk`) REFERENCES `manufacturers` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_products_validRatings` FOREIGN KEY (`rating_fk`) REFERENCES `validRatings` (`value`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=418 DEFAULT CHARSET=utf8mb4
A similar question occurred to me with a text field.
(MySQL 1366 error incorrect string value)
I confirmed the database, table, column character set were all utf8mb4.
The problem was solved by changing the field type to longtext.
Hope this could help someone.

HQL is generating incomplete 'cross join' on executeUpdate

I am working on a grails project. I have following query that I am trying to execute
String CHECK_FOR_HIGH_TRADE_VOLUME_QUERY = "Update LocationTrade lt set lt.hasVeryHighVolume=true where lt.locationIndices=? AND lt.trade.volume>20000";
...
LocationTrade.executeUpdate(CHECK_FOR_HIGH_TRADE_VOLUME_QUERY, [indices]);
The relationship between LocationTrade and Trade is unidirectional many-to-one. So, LocationTrade has a reference to Trade but Trade class does not have reference to the List of LocationTrade.
On execution, I get the following exception.
org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute update query; SQL [update location_trade cross join set has_very_high_volume=1 where location_indices_id=? and volume>20000]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute update query
and
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'set has_very_high_volume=1 where location_indices_id=997 and volume>20000' at line 1
It seems that generated query is wrong. There should have been a join with the Trade table, but that is missing. I am unable to identify the error that I made here. Can some of you help me?
Creation Script for the two tables (I have stripped some of the uninteresting columns)
CREATE TABLE `location_trade` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`auto_status` varchar(255) DEFAULT NULL,
`exclusion_reason_description` varchar(255) DEFAULT NULL,
`exclusion_reason_id` bigint(20) DEFAULT NULL,
`exclusion_reason_title` varchar(255) DEFAULT NULL,
`location_indices_id` bigint(20) DEFAULT NULL,
`manual_status` varchar(255) DEFAULT NULL,
`trade_id` bigint(20) DEFAULT NULL,
`absolute_price` decimal(19,6) DEFAULT NULL,
`flag` varchar(255) DEFAULT NULL,
`auto_exclusion_reason` varchar(255) DEFAULT NULL,
`date_created` datetime DEFAULT NULL,
`exclusion_reason_text` varchar(255) DEFAULT NULL,
`last_updated` datetime DEFAULT NULL,
`has_very_high_volume` bit(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK858985A90CAA966` (`location_indices_id`),
KEY `FK858985AB5FA6A69` (`trade_id`),
CONSTRAINT `FK858985A90CAA966` FOREIGN KEY (`location_indices_id`) REFERENCES `location_indices` (`id`),
CONSTRAINT `FK858985AB5FA6A69` FOREIGN KEY (`trade_id`) REFERENCES `trade` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25405 DEFAULT CHARSET=latin1;
CREATE TABLE `trade` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`comments` varchar(1020) DEFAULT NULL,
`end_date` datetime DEFAULT NULL,
`price` decimal(19,6) DEFAULT NULL,
`price_type` varchar(255) DEFAULT NULL,
`source_id` bigint(20) DEFAULT NULL,
`start_date` datetime DEFAULT NULL,
`trade_date` datetime DEFAULT NULL,
`trade_name` varchar(255) DEFAULT NULL,
`volume` decimal(19,6) DEFAULT NULL,
`volume_units` varchar(255) DEFAULT NULL,
`date_created` datetime DEFAULT NULL,
`last_updated` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK697F1642D085935` (`source_id`),
CONSTRAINT `FK697F1642D085935` FOREIGN KEY (`source_id`) REFERENCES `job_source` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=26567 DEFAULT CHARSET=latin1;
Thanks
The Hibernate documentation says:
No join, either implicit or explicit, can be specified in a bulk HQL query. Sub-queries can be used in the where-clause, where the subqueries themselves may contain joins.
lt.trade.volume is an implicit inner join between LocationTrade and Trade, so the query is invalid. You'll have to rewrite it to something like the following:
update LocationTrade lt set lt.hasVeryHighVolume=true where lt.locationIndices=?
and lt.id in (
select lt2.id from LocationTrade lt2 where lt2.trade.volume > 20000)
Or you'll have to use a SQL query instead.

com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect date value: '' for column 'Date_Of_Birth' at row 1

I'm trying to update the personal detail of a user through a java panel.Panel has fields like user_id(autogenerated),name,date of birth.
problem is when i enter nothing to the date of birth field in java panel and then save it. It gives me the above mentioned error.
i tried to verify it by inserting null to the date of birth(Date datatype) field directly using the mysql database.There it gives no error.
Why is it not taking null string when i insert through java panel but is taking when insert directly using mysql.
CREATE TABLE `userpersonaldetail` (
`User_Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(45) default NULL,
`Date_Of_Birth` date default NULL,
`Address` varchar(300) default NULL,
`Phone` varchar(20) default NULL,
`Mobile` varchar(20) default NULL,
`Email` varchar(50) default NULL,
PRIMARY KEY (`User_Id`),
CONSTRAINT `FK_userpersonaldetail_1` FOREIGN KEY (`User_Id`) REFERENCES `usermaster` (`User_Id`)
)
And the portion of the code where exception occurs is:
try
{
con=(Connection) DBConnection.getConnection();
pstmt=(PreparedStatement) con.prepareStatement("Update userpersonaldetail set "+
"name=?,date_of_birth=?,address=?,phone=?,mobile=?,email=? where user_id=?");
pstmt.setInt(7,perBean.getUserId());
pstmt.setString(1,perBean.getName());
pstmt.setString(2,perBean.getDateOfBirth());
pstmt.setString(3,perBean.getAddress());
pstmt.setString(4,perBean.getPhone());
pstmt.setString(5,perBean.getMobile());
pstmt.setString(6,perBean.getEmail());
int i=pstmt.executeUpdate();
}
here perBean is the javaBean which retrieves values from the gui.In one of the test case i kept the date_of_birth text box null which is giving error while storing in DB.
My initial guess would be the field has been defined as 'NOT NULL' which means it will force you to enter a value...
If you were to do a mysql dump of the table (or view it in some tool) you'll probably find it defined such as:
`someDT` datetime NOT NULL
I replaced older version of my-sql-connector jar (to be found in lib folder of the server) with the latest. That solved my problem.
Ahh, i see now, you can't insert '' as a date. you will need to pass a date.
If an attribute does not have a value but you have it mentioned in the column list, forcing you to give something there, you need to use
statement.setNull(index, datatype)
to it. Setting to "" is not the same thing as setting to null.

MySQL field type

CREATE TABLE IF NOT EXISTS `user` (
`USER_ID` bigint(20) NOT NULL auto_increment,
`USER_ABOUT_YOU` varchar(255) default NULL,
`USER_COMMUNITY` tinyblob,
`USER_COUNTRY` varchar(255) default NULL,
`USER_GENDER` varchar(255) default NULL,
`USER_MAILING_LIST` bit(1) default NULL,
`USER_NAME` varchar(255) default NULL,
`USER_PASSWORD` varchar(255) default NULL,
PRIMARY KEY (`USER_ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Why is the USER_COMMUNITY defined as tiny blob. This field accepts a value of checkbox. When i change it to some other datatype i get an error? Why is to so?
http://www.vaannila.com/spring/spring-hibernate-integration-1.html
That's how it is defined:
#Column(name="USER_COMMUNITY")
public String[] getCommunity() {
return community;
}
public void setCommunity(String[] community) {
this.community = community;
}
The table doesn't store the checks but an array of Strings. And it looks like, TINYBLOB is the correct datatype on MYSQL for storing arrays.
There is no 'value of checkbox'. A checkbox returns a string as do most other HTML input controls. These checkboxes actually return a list of values, because they all have the same name. I don't know how this value is stored actually, but I can imagine that is is stored as an array of string with some accompanieing meta data. This kind of data will be hard to store in another field type.
In what field type did you want to change it, and what was the error you got?
I would actually not store this data this way, but give user a detail table instead in which you can store the communities the user is a member of. But this tutorial seems to focus more on jsp than database normalisation. ;)

Categories

Resources