Hibernate generates invalid SQL query with MySQL - java

I have the following JPA entity classes (example case). A House belongs on a single Street. A Street has many Houses.
#Entity
public class House {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
public String name
#ManyToOne
public Street street;
}
#Entity
public class Street {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Integer id;
#OneToMany(mappedBy="street")
public Set<House> houses;
}
I have the generation type set to identity, which is supposed to auto assign a new ID.
When creating a new House with a new Street, I have to first create and persist Street, followed by House. This is because I do not have CascadeType set to PERSIST, so it has to be done manually [1]. However, while inserting a newly created Street:
Street street = new Street();
entityManager.persist(street);
Hibernate/JPA generates the following SQL query:
insert into Street default values
which MySQL doesn't like.
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 'default values' at line 1
Any ideas why? I'm using Java 6, MySQL 5.0.67 with Hibernate's implementation of JPA (version 3.2.1.ga).
[1] EJB 3 in Action pages 318-319

If you don't have an error in your SQL statement, then you might want to check your table column name as it can contain a predefined name that Mysql uses; for example 'column' which can't be used as a table column name

Standard SQL specifies this optional syntax for INSERT:
INSERT INTO <Table Name> DEFAULT VALUES
This is legal SQL syntax, supported, for example, by Microsoft SQL Server, PostgreSQL, and SQLite, but not by Oracle, IBM DB2, or MySQL.
MySQL supports other syntax that achieve the same result:
INSERT INTO <Table Name> () VALUES ()
INSERT INTO <Table Name> (col1, col2, col3) VALUES (DEFAULT, DEFAULT, DEFAULT)
In Hibernate, you should configure the SQL dialect properly, and it's up to Hibernate to generate valid SQL for the target RDBMS brand.
import org.hibernate.cfg.Configuration;
Configuration cfg = new Configuration();
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
You can also specify properties by placing a file named hibernate.properties in a root directory of the classpath.

Another situation when you might have this exception is when you have reserved words as columns for the table. For example 'to' and 'from' are not suitable clumn names for MySQL. It is obviously bug in Hibernate, that it doesn't check such columns and moreover continues to work with no error even if table is not created.

Make sure spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialecthas correct version of MySQL

Related

Hibernate inserts null for ID when using strategy = GenerationType.IDENTITY

Why is Hibernate inserts null value for the ID in the table.
#Id
#Getter
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
This is the Message I am getting when inserting somthing in table blabla ↓↓↓
Hibernate: insert into blabla (id, bla1, bla2) values (null, ?, ?)
However when instad of using strategy = GenerationType.IDENTITY → strategy = GenerationType.AUTO/SEQUANSE I am getting this message ↓↓↓
Hibernate: call next value for hibernate_sequence
Hibernate: insert into blabla (bla1, bla2, id) values (?, ?, ?)
Does anyone know why it's like that because this null in ID place is causing an Exception when connecting to a database.
Update:
I've run my Application with strategy = SEQUENCE and AUTO but I am getting also an Exception
Database is H2
Solution:
you go to your application.properties file and add this line of code:
hibernate.hbm2ddl.auto=validate
Maybe your table hasn't been defined as an auto incremental PK.
Try to add this one to application.properties:
hibernate.hbm2ddl.auto=validate
Both these commands were fixed in Hibernate ORM 5.6.5.Final, older versions produce invalid SQL for a some historic reason, it was accepted by old versions of H2, but new versions throw an expected exception.
This version also has various other changes needed for recent versions of H2.
You need to upgrade hibernate-core to this or newer version.

Calculating age from birthdate with #Formula

I'm trying to calculate the user's age from her birthdate, using #Formula.
According MySQL documentation, I have tried this:
#Entity
#Table(name = "users")
public final class UserProfile {
//this column is called BIRTHDATE in database
private String birthdate;
#Formula("(select TIMESTAMPDIFF(YEAR,BIRTHDATE,CURDATE()) from users)")
private String age;
//more things...
}
But I get an error:
java.sql.SQLSyntaxErrorException: You have an error in your SQL
syntax; check the manual that corresponds to your MariaDB server
version for the right syntax to use near
'userprofil0_.YEAR,userprofil0_.BIRTHDATE,CURDATE()) from users) as
formula0_0_ f' at line 1
If I execute that query in phpMyAdmin, it works properly.
Following this, I have also tried with:
#Formula("date_part('year', age(birthdate))")
private String age; //I tried with int instead of String, too
But I have this error:
java.sql.SQLSyntaxErrorException: FUNCTION
userprofile-service-db.date_part does not exist
This one does not work even in phpMyAdmin...
Any idea?
EDIT
I have just tried with:
#Formula("(TIMESTAMPDIFF(YEAR,BIRTHDATE,CURDATE()))")
private String age;
but is still not working. This is the query that Hibernate shows:
select
userprofil0_.id as id1_1_0_,
userprofil0_.about_me as about_me2_1_0_,
userprofil0_.birthdate as birthdat3_1_0_,
userprofil0_.display_name as display_4_1_0_,
userprofil0_.email as email5_1_0_,
userprofil0_.profile_photo as profile_6_1_0_,
(TIMESTAMPDIFF(userprofil0_.YEAR,
userprofil0_.BIRTHDATE,
CURDATE())) as formula0_0_
from
users userprofil0_
where
userprofil0_.id=?
It looks like Hibernate is taking YEAR as a column, but actually it is a reserved word. If I execute that query in phpMyAdmin replacing userprofil0_.YEAR with just YEAR, it works fine.
Your first attempt was almost right, but you don't need select and from parts since column BITHDATE is in the same table:
#Formula("(TIMESTAMPDIFF(YEAR,BIRTHDATE,CURDATE()))")
private String age;
Also would be useful to turn on the logging of generated SQL query (in Hibernate it's done by setting property show_sql to true), to see the whole query.
UPDATE
As a workaround of the treating YEAR as a column name by Hibernate you can wrap call to TIMESTAMPDIFF in a custom MySQL function and call this function in Formula. Here is the SO answer to a similar problem.
Another solution could be to write a custom dialect resolver, which will treat YEAR as a keyword: SO answer
Try this formula to calculate the difference between current date and BIRTHDATE column stored in the database to get user age in year
#Formula("YEAR(CURDATE()) - YEAR(BIRTHDATE)")
private int age;

Sybase DataContext : The column prefix does not match with a table name or alias name

We are dealing with sybase database in core java code. We are using org.eobjects.metamodel.DataContext to parse query.
String sCol[]=table.getColumnNames();
Query query=dataContext.query().from(table.getName()).select(sCol).toQuery();
return new QueryIterator(dataContext.executeQuery(query).iterator());
But it executing query. Same code working fine with Oracle database to parse and execute query.
Some of query example generated are :
select City.CityName from ownername.City
select City.CityName from City
select CityName from ownername.City
select CityName from City
select ownername.City.CityName from ownername.City
SELECT "City"."CityName" FROM ownername."City"
select * from ownername.City
No any of above query executed. We are getting these errors :
Not capable of parsing FROM token: "ownername"."City"
Could not execute query: The column prefix '"City"' does not match with a table name or alias name used in the query. Either the table is not specified in the FROM clause or it has a correlation name which must be used instead.
Not capable of parsing SELECT token: ownername.City.CityName
How can we execute query using metamodel wih SYBASE database OR is there any other way to execute sybase queries?
Oracle (and Microsoft) use a schema logical construct that Sybase ASE does not. In SAP/Sybase ASE, all tables and columns in a database are in the same schema. It is possible to users to have their own objects in the database though, so there is the possibility of imitating some of the schema behavior using user ownership, but it would require an extra level of effort.
For Sybase the proper query syntax would be:
SELECT [ Col1, Col2 | * ]
FROM [dbName].[ownerName.]TABLE
In your case
SELECT CityName
FROM dbName.ownername.City
In Sybase ASE, it's typically best practice to have all objects owned by 'dbo', so in that case you can omit the owner from the query:
SELECT CityName
FROM dbName..City
Full query syntax and information can be found in the Query Section of the Transact SQL Users Guide in the documentation.
The error messages you're getting are coming from MetaModel's query parser layer. It is searching for matching column and table names in the metadata of your database before it's even firing the query.
I notice that you're using namespace "org.eobjects.metamodel". You should upgrade to Apache MetaModel ("org.apache.metamodel") if possible since a lot has been improved in MetaModel since it's introduction into Apache. Including a lot of query parser improvements.

MySQL LONGTEXT with h2 embedded database

I am trying to make my application run on MySQL (for production) and H2 (for dev/testing). My (Flyway) script to create the tables is almost identical now, except for a column that needs to be declared 'LONGTEXT' for MySQL. If I also use this for H2 (which is running in MySQL compatibility mode), I get:
Wrong column type in public.public.customer_license for column license.
Found: clob, expected: varchar(65535)
The Java code of my entity:
#Column(name = "license", length = 65535)
private String m_license;
If I change the column declaration to VARCHAR(65535), then it works for H2, but not for MySQL:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Column length too big for column 'license'
(max = 21845); use BLOB or TEXT instead
How can I make it work for both?
I had the same problem. I solved it by using the #Lob Annotation. This validates ok with LONGTEXT in a mysql table. When using an H2 in-memory, a CLOB field is created.
import javax.persistence.Lob;
...
#Lob
private String lotsOfText;
That is one of the reasons there is orm.xml, so you can have one mapping for one datastore, and one for a different datastore, and hence no need to recompile the code between runs

Intellij IDEA jpa console issue select from tables with specific columns

We have a project includes Eclipselink and MySQL.
For example if we use simple query:
SELECT a from ExampleTable a
it is transformed to something like this:
SELECT `id`, `code`, `high`, `key`, `name`, `regionalCode` FROM `ExampleTable`
and if I am using JPA Console I am getting something like that:
SELECT id, code, high, key, name, regionalCode FROM ExampleTable
and error message
[42000][1064] 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 'key, name, regionalCode FROM ExampleTable' at line 1
The problem is "key" column but I have no chance to rename it.
I tried to find options to fix that but have no results.
I think if there will be no answer - the good way is to create bug report.
How we get enclosed names of columns?
We are using EntityManager
EntityManager em;
...
TypedQuery = em.createQuery("SELECT et FROM ExampleTable et");
And if we run it we see (in debug mode) that all names in generated query are enclosed with ` symbol.
key is reserved keyword of MySQL database (see MySQL: Reserved Words).
so you should escape reserved keyword in your mapping.
You can do it in the following way:
#Table
#Entity
public class ExampleTable implement Serializable
{
// ...
#Column(name = "'key'") // or #Column(name = "\"key\"")
private String key;
//...
}

Categories

Resources