Background
I'm writing a project using Spring MVC (Framework v4.0.6.RELEASE, JPA v1.6.2.RELEASE) and Hibernate (Core v4.3.6.FINAL, JPA API v2.1). In my project, there are entities called 'Project'. Each of these projects have their unique, auto-generated IDs as primary keys. This ID is generated by the following code:
#Id
#Column(name = "project_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long projectId;
This code works as expected and automatically creates unique IDs.
Problem
Each of these projects are supposed to have a random, unique1 'secret' String, just like those assigned by API providers like Facebook, Twitter, etc. So, to achieve this, I tried using the following code, as per the Hibernate docs:
#Column(name = "project_secret", nullable = false, unique = true)
#GenericGenerator(name = "uuid-gen", strategy = "uuid")
#GeneratedValue(generator = "uuid-gen")
private String projectSecret;
However, whenever I try to create a new project entity, I'm greeted by a org.springframework.dao.DataIntegrityViolationException with root cause:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Column 'project_secret' cannot be null
This should be auto-generated by Hibernate on creation, must be random and unique1. A 128-bit UUID is enough for me (32 characters w/out dashes) and I read that Hibernate has a UUID generator, so that's what I was aiming to use.
Further Info
After searching for hours, I'm no closer to solving it the way I want to do it. I found one possible solution, which is to include:
#PrePersist
private void generateSecret(){
this.setProjectSecret(UUID.randomUUID().toString());
}
in the Project entity class. When this method is inserted (and #GenericGenerator & #GeneratedValue tags removed), the project secret is correctly generated and inserted; system works as expected; no exceptions are thrown. However, (I believe) this can't ensure uniqueness2 and just causes an exception when a duplicate secret is inserted. I want to ensure uniqueness and preferably want to solve this with built-in Hibernate generators.
(Notes)
I actually am not sure if uniqueness should be enforced. I suppose having every secret unique can (theoretically) create an additional layer of security, which takes me to:
I realise that the probability of UUID collision is very very low, so UUID generation ensures that uniqueness in a probabilistic sense but can (or should) I be really sure of it?
I had an issue like this before and I realised after a while that it was my database table that was causing the issue. This might be the same problem you are having...
For your project_id ensure you use the following when you are creating that column in the database
GENERATED ALWAYS AS IDENTITY
I hope this is the same issue and that this will be of help to you. Also would recommend using uuid2 as your strategy.
see here... http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#d0e5294
Edit
After realising that project_secret is not the #id field then the answer is that hibernate does not support generated values on any column except for the #id field.
See here for more details : Hibernate JPA Sequence (non-Id)
Related
After updating dependencies from Spring 4.1.6.RELEASE to 5.1.4.RELEASE and Hibernate 4.3.9.Final to 5.4.1.Final, some of my test cases started failing with ConstraintViolationException on the primary key.
Due to historical reasons we are generating the primary like so:
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "NAME_HERE")
#TableGenerator(name = "NAME_HERE", table = "SEQUENCE_TABLE_NAME", pkColumnName = "Name", pkColumnValue = "ENTITY_NAME", valueColumnName = "VALUE_COLUMN_NAME", allocationSize = 100)
While using the application normally it does not seem to be a problem, however when running tests it does.
I have noticed that the ConstraintViolationException only occurs for tables where multiple values are inserted within the same test case. So i'm thinking the rows are somehow assigned the same primary key.
Running the tests locally it does not seem to be a problem, so it could also have something to do with how the tests are run on the build server. However it was not a problem before upgrading dependencies.
I have checked that the sequence values are higher than the latest value in the database, but again this was not a problem before upgrading dependencies.
I am hoping to jolt someones memory :-)
i found this:
Hibernate, #SequenceGenerator and allocationSize
Which made me find "hibernate.id.new_generator_mappings"
As per:
https://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#configurations
*Setting which indicates whether or not the new org.hibernate.id.IdentifierGenerator are used for AUTO, TABLE and SEQUENCE.
Existing applications may want to disable this (set it false) for upgrade compatibility from 3.x and 4.x to 5.x.*
Setting this to false solved the problem.
I have a weird behavior of Hibernate (3.6.1.Final) in my application and I am pretty desperate at the moment. The behavior occurs on both MariaDB 10.1 and RDS on Amazon.
After a certain amount of successful persists (always around ~5k) Hibernate fires a:
SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails
From what I see in the stacktrace Hibernate tries to fill a foreign key in a entity with a negative value that violates the FK constraint (the target table of the FK does not have such primary key value).
The strange thing is:
It happens only after around ~5k successful persists
After submitting such number of persist operation, the error occurs even if I restart the application (as well as the DB). Only solution is to wipe out the DB and repeat.
The error occurs independently from the load (continuous, in batches, single or multithread).
The following is the FK of the entity that is violated during the insert:
#Entity
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Charge extends Entry {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "totalId")
private Amount total;
//....
And this is how the primary key is defined in the Amount entity:
#GeneratedValue
#Id
#Column(nullable = false)
private Integer pk;
//....
My questions are:
Why a non-valid negative value occurs in the FK ? (the primary key targeted by the FK is set as auto increment, starting from one and all the preceeding inserts worked perfectly accordingly to this)
Why does the error occurs only after ~5k persists ?
Why not even a restart of the application fix the problem? DB is fine because a manual insert outside the application works (with valid FK values)
My suspicion is Hibernate and how it manages the auto_increment on the DB. The are many posts on the topic but none of them fit my specific case (error occurring only after a certain usage).
Last note: On the DB auto_increment is initially set to 1. All the initial persists (before the error appears) of the Amount entity have a incremental PK that start by 1: (1,2,3, ...). So again why after a while Hibernate comes up with a negative (incompatible to the FK)?
Thank you so much in advance for your help.
Best
G.
Maybe is the limite of type int ?
Try this :
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
#Column (name = "id")
private long id;
we had exact the same issue and it was due to old mariadb jdbc driver. We were using version 1.4.2
After switching to the version 1.5.5 of mariadb jdbc driver the problem were gone
Let's say that this is a class that has unique constrained field.
#Entity
public class Thing {
#Column(name = "name", unique = true)
private String name;
#ManyToOne
private Owner owner;
}
Example works just fine if new Things are created with unique names. But when different owners want to create things with the same name this approach fails.
Is it possible to set unique constraint to differ records of Things in the database based on the Owners using Hibernate/JPA functionalities (I could not find any) or should I write my own logic and dump the unique from #Column.
Perhaps it could be done with Hibernate Validator? Reading the docs I haven't found much about unique constraints.
You're looking for #UniqueConstraint
http://docs.oracle.com/javaee/5/api/javax/persistence/UniqueConstraint.html
Similar to this problem:
ORA-00904: : invalid identifier Issue with Hibernate Dependent objects program
I'm trying to do batch inserts using hibernate. Whenever I try to do a session.save or session.saveOrUpdate, hibernate complains that I have an invalid identifier
Stack output
2015-11-20 14:17:37 ERROR : ORA-00904: "USERATTRID": invalid identifier
Entity
public class Attribute {
#Id
#GenericGenerator(name = "increment", strategy = "increment")
#GeneratedValue(generator = "increment", strategy = GenerationType.SEQUENCE)
private int USERAttrID;
private long userNumber;
private String attribute;
private String value;
private String description;
private LocalDateTime updatedDate;
private LocalDateTime createdDate;
The database looks like this after hibernate applies its improved naming convention
USER_ATTRID
ATTRIBUTE
USER_NUMBER
DESCRIPTION
VALUE
CREATED_DATE
UPDATED_DATE
If I use repository.save then the code works fine. The crud repository seems to know how to map my entity to the database, but hibernate's session does not.
I see a few solutions here:
1. Use hibernate's default default naming strategy. Tables get named things like USERATTRID. I'm not sure what implications this has other than making columns harder to read.
2. Combine hibernate with crudrepository using the tutorial here
http://frightanic.com/software-development/jpa-batch-inserts/
3. Figure out how to get hibernate's session.save to use the improvedNamingConvention mapper. Can anyone help me with this or provide me another suggestion?
4. I guess I can also manually map the columns :/. Ugh this is messy, you now have to manually do all the conversions on dates :/
It would be good to know how you are actually getting the session?The most likely cause that comes to mind is that when you using the hibernate session it is actually not built using the same configuration that was used to build the entity manager that CrudRepository uses underneath.
Ends up being more trouble than it's worth as you stop using hibernate jpa's defaults and all the things spring-data provides you out of the box
Stupid. The tutorial doesn't even use crudRepository
Same as 1
Same as 1
Instead I'm banging my head against a wall trying to get a simple batch process to insert
I have a table with a simple int id column with Identity auto increment in SQL Server.
The entity's Id is annotated with #Id and #GeneratedValue
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
In SQL Server the column is properly set as Identity with Seed and Increment equals to 1.
When I try to persist an instance of this entity, Hibernate tries to query the hibernate_sequence table to obtain the ID value. Since I haven't created that table in my schema I'm getting an error:
could not read a hi value: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'MySchema.hibernate_sequence'
If I change the generation type to IDENTITY everything works as expected
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
I cannot change it this way, since my App will run both on MS SQL and ORACLE, and the latter does not support auto incremented columns.
As far as I know the AUTO type should use the auto increment behaviour if the underlying database has support to it, so I don't know why is not working.
UPDATE:
It took me some time but I was able to understand exactly what is going on.
I am working with legacy databases with the following behaviours:
MSSQL: id generation uses table IDENTITY
ORACLE: id generation uses a trigger. The trigger queries and updates a custom table where all the "next ids" are stored. This table is called SEQ.
Here is the outcome of using some id generation strategies:
AUTO: does not work in MSSQL, as explained above
IDENTITY: works in MSSQL but is not supported by Oracle
"native": works in MSSQL but fails in ORACLE. It fails because Hibernate activates its default sequence strategy, which uses hibernate_sequences.nextval. Since this is a legacy application the values from the SEQ table (mentioned above) and the hibernate_sequences are not synchronized (SEQ's value for that particular table is at 6120, and hibernate_sequences' is at 1, which is expected since it was not used until now).
So what I need to figure out is a way to configure that entity to:
Use MSSQL Identity feature
OR
When using Oracle, do not automatically set any value to the ID variable and leave everything up to the pre-existing trigger
This can cause me serious issues on Oracle when I need to insert entities that depend on the main entity (via foreign key), because Hibernate won't know which ID value was generated by the "external" trigger.
I had a similar problem and found this information (deeper explained in here).
Adding this property into my persistence.xml file fixed the issue:
<property name="hibernate.id.new_generator_mappings" value="false" />
Orcale 12c supports IDENTITY and SQL SERVER 2012 supports SEQUENCES. I believe a SEQUENCE is always a better choice than an IDENTITY. IDENTITY disables batching and SEQUENCES allow you to provide optimizers, such as the pooled-lo optimization strategy.
This is how the actual identifier generator is chosen for the configured GenerationType value:
switch ( generatorEnum ) {
case IDENTITY:
return "identity";
case AUTO:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "native";
case TABLE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.TableGenerator.class.getName()
: MultipleHiLoPerTableGenerator.class.getName();
case SEQUENCE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "seqhilo";
}
If you use the new identifier generators:
properties.put("hibernate.id.new_generator_mappings", "true");
The AUTO will actually use a SequenceStyleGenerator and where the database doesn't support sequences, you end up using a TABLE generator instead (which is a portable solution but it's less efficient than IDENTITY or SEQUENCE).
If you use the legacy identifier generators, you then end up with the "native" generation strategy, meaning:
public Class getNativeIdentifierGeneratorClass() {
if ( supportsIdentityColumns() ) {
return IdentityGenerator.class;
}
else if ( supportsSequences() ) {
return SequenceGenerator.class;
}
else {
return TableHiLoGenerator.class;
}
}
If a new Oracle12gDialect is going to be added and it will support IDENTITY, then AUTO might switch to IDENTITY rather than SEQUENCE, possibly breaking your current expectations. Currently there is no such dialect available so on Oracle you have SEQUENCE and in MSSQL you have IDENTITY.
Conclusion:
Try it like this:
#Id
#GenericGenerator(name = "native_generator", strategy = "native")
#GeneratedValue(generator = "native_generator")
private Long id;
make the id a Long instead of Integer, and you can let the HBMDDL handle the primary key column type.
force Hibernate to use the "native" generator
If your legacy system uses a table for generating sequence values and there was no hilo optimization ever used you can use a table identifier generator:
#Id
#GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
#TableGenerator(name = "table", allocationSize = 1
)
private Long id;
You can also use the JPA table generator, just make sure you configure the right optimizer. For more info check my Hibernate tutorial
because
#GeneratedValue(strategy = GenerationType.AUTO)
use SequenceStyleGenerator by default in earlier versions
you have to look at this https://hibernate.atlassian.net/browse/HHH-11014