How to specify array of ENUM in liquibase? - h2

I am trying to create a table with a column of type enum array. Sample is
<sql>CREATE TYPE part_state_type AS ENUM ('A','B','C','D');</sql>
<createTable tableName="parts">
<column name="part_state" type="part_state_type []">
</column>
</createTable>
When I run the migrations; I get following error:
Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "CREATE TABLE PUBLIC.PARTS (PART_STATE ENUM('A', 'B', 'C', 'D') [[*]]) "; expected "FOR, UNSIGNED, INVISIBLE, VISIBLE, NOT, NULL, AS, DEFAULT, GENERATED, ON, NOT, NULL, AUTO_INCREMENT, BIGSERIAL, SERIAL, IDENTITY, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, ,, )"; SQL statement:
CREATE TABLE PUBLIC.parts (part_state ENUM('A', 'B', 'C', 'D') []) [42001-197]
Any idea how to create a column of type ENUM array? I am using Java 11, liquibase version is 3.5.3 and h2 version is 1.4.197. Based on this https://oliha.dev/articles/enums-as-arrays-in-postgresql/ it seems that having array of enum is possible in postgresql. So, I am not sure why is liquibase failing.

ENUM values are broken in persistent databases in 1.4.197, this issue is fixed in later versions.
Arrays of ENUM values are broken in all versions of H2, I filled a new issue about it in the bugtracker:
https://github.com/h2database/h2database/issues/2935
Note that ENUM(something) ARRAY syntax is only supported by current H2 and it will be supported by H2 2.0. Released versions of H2 don't have typed arrays, you can use the plain ARRAY data type in them, but you can run into the same issue with persisted values. In-memory database hypothetically may be not affected.

Related

How to add integer array column to H2 Database

ALTER TABLE my_table ADD COLUMN my_integers Integer[];
ALTER TABLE my_table ADD COLUMN my_integers Array[Integer];
Do not work.
ALTER TABLE my_table ADD COLUMN my_integers array;
Works. But there's no type.
H2 1.x.y does not have typed arrays, there is no way to define them. You can use only the plain ARRAY data type. Of course, you can use it for integer values too.
H2 since the version 2.0.202 supports only standard-compliant typed arrays with the syntax from the SQL Standard (INTEGER ARRAY, INTEGER ARRAY[10] etc.)
https://h2database.com/html/datatypes.html#array_type
Also take a look on the array literal grammar:
https://h2database.com/html/grammar.html#array
Please note that syntax of array literals was changed in H2 1.4.198 from H2-specific to the standard one. The online documentation is for the latest released version. If you use 1.4.197 or older version for a some reason, it has another syntax for array literals.

Error when add data by JPA repository [duplicate]

#Column(name="open")
Using sqlserver dialect with hibernate.
[SchemaUpdate] Unsuccessful: create table auth_session (id numeric(19,0) identity not null, active tinyint null, creation_date datetime not null, last_modified datetime not null, maxidle int null, maxlive int null, open tinyint null, sessionid varchar(255) not null, user_id numeric(19,0) not null, primary key (id), unique (sessionid))
[SchemaUpdate] Incorrect syntax near the keyword 'open'.
I would have expected hibernate to use quoted identifier when creating the table.
Any ideas on how to handle this... other than renaming the field?
With Hibernate as JPA 1.0 provider, you can escape a reserved keyword by enclosing it within backticks:
#Column(name="`open`")
This is the syntax inherited from Hiberate Core:
5.4. SQL quoted identifiers
You can force Hibernate to quote an
identifier in the generated SQL by
enclosing the table or column name in
backticks in the mapping document.
Hibernate will use the correct
quotation style for the SQL Dialect.
This is usually double quotes, but the
SQL Server uses brackets and MySQL
uses backticks.
<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class>
In JPA 2.0, the syntax is standardized and becomes:
#Column(name="\"open\"")
References
Hibernate reference guide
5.4. SQL quoted identifiers
JPA 2.0 specification
2.13 Naming of Database Objects
Related questions
Hibernate, MySQL and table named “Repeat” - strange behaviour
Automatic reserved word escaping for Hibernate tables and columns
Had the same problem, but with a tablename called Transaction. If you set
hibernate.globally_quoted_identifiers=true
Then all database identifiers will be quoted.
Found my answer here
Special character in table name hibernate giving error
And found all available settings here
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/appendices/Configurations.html
Could not find better docs for this though.
In my case the setting was in my Spring properties file. As mentioned in the comments, it could also be in other, hibernate related, configuration files.
Manually escaping the reserved keywords
If you are using JPA, you can escape with double quotes:
#Column(name = "\"open\"")
If you're using Hibernate native API, then you can escape them using backticks:
#Column(name = "`open`")
Automatically escaping reserved keywords
If you want to automatically escape reserved keywords, you can set to true the Hibernate-specific hibernate.globally_quoted_identifiers configuration property:
<property
name="hibernate.globally_quoted_identifiers"
value="true"
/>
Yaml format
spring:
jpa:
properties:
hibernate:
globally_quoted_identifiers: true
If you use as shown below it should work
#Column(name="[order]")
private int order;
#Column(name="\"open\"")
This will work for sure, Same problem happened with me, when I was learning hibernate.
There is also another option: hibernate.auto_quote_keyword
which
Specifies whether to automatically quote any names that are deemed keywords.
<property name="hibernate.auto_quote_keyword" value="true" />
Yaml
spring:
jpa:
properties:
hibernate:
auto_quote_keyword: true
No - change the column name.
This is database-specific, and you just can't create such a column. After all hibernate finally sends DDL to the database. If you can't create a valid DDL with this column name, this means hibernate can't as well. I don't think quoting would solve the issue even if you are writing the DDL.
Even if you somehow succeed to escape the name - change it. It will work with this database, but won't work with another.
Some JPA implementations (e.g the one I use, DataNucleus) automatically quote the identifier for you, so you never get this.

How can I prevent OpenJPA from replacing "constant" parameters in my queries? [duplicate]

This question already has an answer here:
jpa namedquery with literals changed to prepared statement
(1 answer)
Closed 7 years ago.
I seem to have a problem with a query on a CHAR field that is incorrectly filtered when OpenJPA replaces my constant value with an SQL parameter.
Example
Given this table in Oracle
create table PERSON (
id char(10) not null,
type char(3) not null,
primary key (id)
)
with three different values for type: WTW, WAI, V
and the corresponding entity
#Entity
public class Person {
String id;
String type;
}
I use the following query from an orm.xml file:
<named-query name="person.v">
<query>
select p
from Person p
where p.type = 'V'
</query>
</named-query>
Problem
When I run this through an EntityManager provided by OpenJPA, the query changes to
select p.id, p.type
from PERSON p
where p.type = ?
and OpenJPA passes the value "V" as parameter. The previously "constant" value for type is now an SQL parameter. The problem lies in the fact that for the char(3) type column, Oracle will store
"V "
and this is not equal to the value passed by OpenJPA as a parameter. Again: without parameters, i.e. just using the string in SQL that I use in JPQL, everything works just fine.
I assume OpenJPA performs this replacement in order to minimize the query cache by normalizing all queries, and I understand that this makes a big difference for a lot of people, but I think this is a problem in my case.
My question is now: how can I prevent OpenJPA from doing this replacement? I know that I won't have different permutations of this query at runtime. Is there a configuration property or a query hint that I can use?
Besides just changing the column to a VARCHAR, I have two workarounds that are not very nice:
Adapt the JPQL to where p.type = 'V ' ("V" followed by two spaces).
Use a native query which won't be optimized by OpenJPA.
In (1), I know about the underlying char(3) and the idea of JPQL is to abstract this away, so this goes a little bit against the idea of using JPA.
With (2) I have to rewrite this simple query just a little bit, but it just does not seem right to use a native query just to avoid problems created by good intentions of my persistence provider.

h2 does not enforce NOT NULL in MySQL compatibility mode

In MySQL compatibility mode, the following SQL succeeds and returns 0:
CREATE TABLE test2 (i INTEGER NOT NULL);
INSERT INTO test2 VALUES (NULL);
SELECT * FROM test2;
It fails as expected in the default mode. It also fails with MySQL 5.5 / InnoDB. Does "MySQL compatibility" actually mean "MyISAM compatibility"?
This problem is due to both the way MySQL handles Null values and h2. Basically MySQL converts nulls received to empty string/0 where as in h2 there is clear distinction among all three types.
So, they(h2) have decided to convert the nulls to 0/empty string/current time stamp in MYSQL mode. But since the as per core h2 0/empty string are not nulls, so it inserts the data.
Use org.h2.engine.Mode and modify the variable related to this:
Mode mode = Mode.getInstance("MYSQL");
mode.convertInsertNullToZero = false;
This has worked for me. Thanks to this link.
From H2: MySQL Compatibility Mode:
When inserting data, if a column is defined to be NOT NULL and NULL is inserted, then a 0 (or empty string, or the current timestamp for timestamp columns) value is used. Usually, this operation is not allowed and an exception is thrown.
The MySQL manual says the following about INSERT, with no distinction between MyISAM or InnoDB:
Inserting NULL into a column that has been declared NOT NULL. For multiple-row INSERT statements [..], the column is set to the implicit default value for the column data type. [..] (For a single-row INSERT, no warning occurs when NULL is inserted into a NOT NULL column. Instead, the statement fails with an error.)
Thus I'm not sure why, or from whence, this choice was made by H2.

comparing timestamp and date in Derby

I need to compare a date and a timestamp in a SELECT query. Normally, this is supported by most other DB platforms(MySQL, MSSQL, Oracle, etc.) but in Derby it throws this error:
Caused by: java.sql.SQLSyntaxErrorException: Comparisons between
'DATE' and 'TIMESTAMP' are not supported. Types must be comparable.
String types must also have matching collation. If collation does not
match, a possible solution is to cast operands to force them to the
default collation (e.g. SELECT tablename FROM sys.systables WHERE
CAST(tablename AS VARCHAR(128)) = 'T1')
I don't want to use CASTs because I cannot change the SQL query. Is this a bug from Derby that will get fixed sometime? I'm currently using Derby 10.8.2.2.
Thanks!
If you are connecting from a java program you can get specific parts of a Timestamp using various getxxx() methods (these are actually inherited from Java.util.date)
See the docs http://docs.oracle.com/javase/7/docs/api/java/util/Date.html
and check the getDate(), getDay() getHour().... methods
Perhaps you can define a VIEW on one of the tables, and include the CAST in the VIEW. That way, you don't have to change the actual query; it will go against the view, and thus include the cast in that way.

Resources