How to generate case sensitive columns with JPA in MySQL automatically - java

How can I order JPA to set a MySQL database column with text content as case sensitive by default upon creation?

The #Column annotation on your field can specify a columnDefinition attribute which may allow you to specify a case-sensitive collation for the column.
public abstract String columnDefinition
    (Optional) The SQL fragment that is used when generating the DDL for the column.
    Defaults to the generated SQL to create a column of the inferred type.
    Default:
        ""
In your case, for example, using the #Column annotation, you would use
#Column(name = "NAME_COL", columnDefinition = "VARCHAR(250) COLLATE latin1_general_cs")
private String name;

MySQL varchar type is not case-sensitive. What you want is varbinary.
CREATE TABLE IF NOT EXISTS `stuff` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varbinary(255) NOT NULL -- varbinary to be case sensitive
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Related

generate auto-incremented field in java spring data

I need to create a auto-incremented key (not a primary key) to use it as a file-name in spring-data
here what i tried:
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "BODY_FILE")
private long bodyFile;
in liquibase:
- column:
name: BODY_FILE
type: BIGINT
autoIncrement: true
but the field bodyfile is always 0.
#Generated value will ONLY work with a primary key.
It won't work with fields that are not a primary key.
It won't even work with the composite primary key.
But why not generated type can be applied for the non-primary field?
A possible answer is most of the older version of DB either does not support the AUTO_INCREMENT (Generated value) for the non-primary key field or If they support in a newer version that too has constraints- like for MySQL There can be only one AUTO_INCREMENT column per table, it must be indexed, and it cannot have a DEFAULT value.
So from where this zero comes from in your DB?
It is because of your datatype 'Long' its default value is getting stored in your DB.
For more details on #Generated value official documentation

How to set the name of a class level jpa check constraint

I would like to define a constraint in JPA on an entity to make sure that either one of two properties "text" or "title" is set to a non-null value.
For this example, suppose the following Question entity class:
#Entity
#Table
public class Question {
#Id
private Long id;
#Column(nullable = true)
private String text;
#Column(nullable = true)
private String title;
}
For this class, JPA will generate the following SQL statement (we need to use the oracle dialect):
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id)
);
In order to check that either one of the properties is set, I could add a check constraint:
#Entity
#Table
#Check(constraints = "TEXT IS NOT NULL OR TITLE IS NOT NULL")
public class Question {
...
}
Now JPA will generate this:
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id),
check (TEXT IS NOT NULL OR TITLE IS NOT NULL)
);
On the database side, this will generate a check constraint with a random name like "SYS_C00127157".
In order to assign a meaningful name (for example: check_title) to the constraint, I could use this DDL:
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id),
constraint check_title check(TEXT IS NOT NULL OR TITLE IS NOT NULL)
);
What I am looking for is something like this:
#Entity
#Table
#Check(name = "check_title" constraints = "TEXT IS NOT NULL OR TITLE IS NOT NULL")
public class Question {
...
}
Unfortunately, this is not possible. The #Check annotation in Java does not offer such a name property for the constraint.
Is there any other way to set a name so that the generated DDL will match the expected result?
It's not possible, unfortunately. If you take a look at how the table generation script is built:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/mapping/Table.java#L604
you'll see that the string, which you specify in #Check annotation is just wrapped like this:
buf.append( ", check (" )
.append( checkConstraint )
.append( ')' );
and the generation of a constraint name is given to the database.
In contrast, a few lines above, you can find that it's possible to influence the name of the unique constraints. Here you can find an example of it.
That's not possible.
In my opinion it's also not a good idea to generate the production database from JPA annotations.
The datamodel usually lives longer than the application and therefore it's good to have the DDL under control.
You should consider to use FlyWay or Liquibase for your database migrations.

Hibernate query ends up in SQL syntax error

I have created a UserPreferences bean. The primary key being id column. Here is the definition of the bean:
#Entity
public class UserPreferences {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(unique = true)
#ColumnDefault("")
private String id;
private String email;
// Getters and Setters
}
Now I use Kotlin to insert data in this bean. Here is the code to do so:
val newUserPreferences = UserPreferences().apply { email = newUserRequest.email }
mUserPreferenceService.save(newUserPreferences)
Lets assume that newUserRequest.email is not null. The auto-dll setting in my application.yml is update. When this code is executed, I see that Hibernate generates following query for creating the table:
create table user_preferences (
id varchar(255) default not null
email varchar(255),
primary key (id)
) engine=MyISAM
At this point, the application fails to load with the following error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
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 'not null,
Looking at the exception, it is very clear that the code is failing because instead of using an empty String as default value, hibernate is simply putting a blank space in the generated query. So here are my questions:
Should this be treated as a bug in Hibernate?
How to handle this situation where I would like to have an empty String "" as column default value?
I'm not a Hibernate user, but I found https://github.com/hibernate/hibernate-orm/blob/master/documentation/src/test/java/org/hibernate/userguide/schema/ColumnDefaultTest.java shows:
#ColumnDefault("'N/A'")
It appears that when you define a default value for a SQL string type, you must use SQL string delimiters (single quotes) inside the Java string. In your case, since you want an empty string, you'd use:
#ColumnDefault("''")
The other way to define a default is to set an attribute in the #Column annotation, as shown in another Stack Overflow answer: How to set default value in Hibernate
So this may not be a bug, but it's a usage of Hibernate that is counter-intuitive for some users, and perhaps not documented clearly.
P.S.: I would count it as a bug that Hibernate still generates CREATE TABLE statements with engine=MyISAM unless you specify the right hibernate.dialect. That storage engine is not recommended. MyISAM is slower than InnoDB in most cases, and fails to support any ACID properties. MySQL has been phasing out MyISAM, and I don't recommend using it for any current projects.

Hibernate not persisting foreign keys in Mysql

I have an entity Property which has city field. Something like this:
#Entity
class Property {
...
#ManyToOne(fetch = FetchType.LAZY)
private City city;
...
}
So each property (e.g house or apartment) belongs to only one city.
Now here is a thing, if I try to log generated SQL by Hibernate, it is generating the foreign key in a right way:
Hibernate:
alter table property
add constraint FKdn1hnohufcwdr4a401xabcjn
foreign key (city_id_city)
references city (id_city)
However, if I check my Mysql database, there is no foreign key there, only index is generated:
show create table property;
leads to:
| property | CREATE TABLE `property` (
`id_property` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`deal_type` varchar(15) NOT NULL,
`publisher_type` varchar(15) NOT NULL,
`type` varchar(15) NOT NULL,
`updated_at` datetime NOT NULL,
`city_id_city` int(11) DEFAULT NULL,
PRIMARY KEY (`id_property`),
KEY `FKdn1hnohufcwdr4a401xabcjn` (`city_id_city`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 |
So the question is why there is no CONSTRAINT FOREIGN KEY () REFERENCES definition in my database?
The problem ended up to be the database engine after I switched to InnoDB engine it started working, as MyISAM does not implement foreign keys.
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
The annotation #ManyToOne is used to map two tables that have a relationship instance by a foreign key, it could not automatically create a foreign key in your database
You need to define the list Property in the City Entity and add the relationship there as well

Hibernate #generatedvalue for HSQLDB

I have the following definition for an id field in an entity that is mapped to a table in HSQLDB.
...
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ID")
private Integer id;
...
But this does not seem to generate the an unique id; instead an attempt is made to insert null into the column which results in failure. If, I manually create a sequence and generation strategy to use that sequence then the data is persisted as expected.
Doesn't a generation strategy of auto imply that the provider (hibernate in this case) will automatically choose the correct approach and do all the heavy lifting as needed (create sequence, use a native approach or whatever works for that particular platform)? Is my understanding incorrect?
Doesn't a generation strategy of auto imply that the provider (hibernate in this case) will automatically choose the correct approach and do all the heavy lifting as needed (create sequence, use a native approach or whatever works for that particular platform)? Is my understanding incorrect?
It does in theory (it defaults to IDENTITY with HSQLDB) and it works for me. This begs the following questions:
What dialect are you using (just in case)?
How did you create the table?
Can you show the DDL (activate the logging of org.hibernate.tool.hbm2ddl if required)?
How do you insert (through Hibernate's API, right?)?
Here is a sample DDL for an entity Foo when using HSQLDB:
create table Foo (
id bigint generated by default as identity (start with 1),
bar varchar(100),
primary key (id)
)
I created the table using the HSQL DB manager. Just normal create table address... I had not set the id column as identity in my case - just set it as primary key.
Then you have your answer, use an IDENTITY column.
While Hibernate does choose the right strategy and does generate the appropriate INSERT statements (passing null into the id which is expected to be persisted into an IDENTITY column), it won't create or alter your physical model if you don't use the DDL generation and export capabilities.
I had the same issue when using a JpaSchemaGenerator utility class that I wrote.
When generating the schema for a org.hibernate.dialect.HSQLDialect (where I use a SEQUENCE to generate my unique IDs), I use the following Hibernate property:
hibernate.id.new_generator_mappings=true
This results in the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID NUMBER(19,0) NOT NULL,
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR2(255 CHAR),
BAT_MAT_ID NUMBER(19,0),
PRIMARY KEY (BAT_ID)
);
But when I use this same property in my utility class to generate a schema using the org.hibernate.dialect.HSQLDialect, I get the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID BIGINT NOT NULL,
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR(255),
BAT_MAT_ID BIGINT,
PRIMARY KEY (BAT_ID)
);
This would mean that if I created a Batch without an ID, it would not generate it for me and the NOT NULL constraint would cause an exception.
If I change the Hibernate property to the following:
hibernate.id.new_generator_mappings=false
Then it would generate the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR(255),
BAT_MAT_ID BIGINT,
PRIMARY KEY (BAT_ID)
);
Which works perfectly when creating JPA entities with Hibernate.

Categories