Catching constraint violations in JPA 2.0 - java

Consider the following entity class, used with, for example, EclipseLink 2.0.2 - where the link attribute is not the primary key, but unique nontheless.
#Entity
public class Profile {
#Id
private Long id;
#Column(unique = true)
private String link;
// Some more attributes and getter and setter methods
}
When I insert records with a duplicate value for the link attribute, EclipseLink does not throw a EntityExistsException, but throws a DatabaseException, with the message explaining that the unique constraint was violated.
This doesn't seem very usefull, as there would not be a simple, database independent, way to catch this exception. What would be the advised way to deal with this?
A few things that I have considered are:
Checking the error code on the DatabaseException - I fear that this error code, though, is the native error code for the database;
Checking the existence of a Profile with the specific value for link beforehand - this obviously would result in an enormous amount of superfluous queries.

When I insert records with a duplicate value for the link attribute, EclipseLink does not throw a EntityExistsException
Yes, and a JPA provider is not supposed to throw an EntityExistException in that case, you won't get an EntityExistException on something else than the primary key.
(...) but throws a DatabaseException, with the message explaining that the unique constraint was violated.
This is very WRONG from EclipseLink, a JPA provider should throw a PersistenceException or a subclass but certainly not a specific exception like o.e.p.e.DatabaseException. This is a bug and should be reported as such as I already mentioned in a previous answer.
This doesn't seem very usefull, as there would not be a simple, database independent, way to catch this exception. What would be the advised way to deal with this?
Same answer as above, see my previous answer.

It's too bad they don't have a ConstraintViolationException in JPA. I've created a helper method to determine if the PersistenceException is a constraint violation for a given class - it is hibernate only though. I imagine there is a way to do it using other implementations.
protected Boolean isUniqueConstraintViolation(PersistenceException ex, Class entity) {
if (((NonUniqueObjectException)ex.getCause()).getEntityName().equals(entity.getName())) {
return true;
}
return false;
}

Related

How to avoid having constraint created by hibernate without having access to the entity

I have an entity where I cant have access to modify a wrong unique=true into the column definition
#Embeddable
class SomePk{
#Column(name="PERSON_ID", unique=true, nullable=false, precision=15)
public long getPersonId() {
return this.personId;
}
}
Using DB-Rider/DBUnit with Hibernate and Spring into JUnitTests the tables and constraints are auto-created before inserts are done according to DB-rider principles.
Am looking for a way of either avoid this constraint creation. Having something like com.my.package.SomePk.PERSON_ID:forceUnique=false
Looked into insert listeners, as well as hibernate reverse engineering config but didnt find a way yet...
The workaround I found is to copy-paste into JUnitTests the entity causing the issue with same package name, remove the uniqueness into this second class and the latter replaced the wrong one. I had this issue into a Springboot JUnitTest.

org.springframework.orm.jpa.JpaSystemException: identifier of an instance of com.cc.domain.User was altered from 90 to null; [duplicate]

org.hibernate.HibernateException: identifier of an instance
of org.cometd.hibernate.User altered from 12 to 3
in fact, my user table is really must dynamically change its value, my Java app is multithreaded.
Any ideas how to fix it?
Are you changing the primary key value of a User object somewhere? You shouldn't do that. Check that your mapping for the primary key is correct.
What does your mapping XML file or mapping annotations look like?
You must detach your entity from session before modifying its ID fields
In my case, the PK Field in hbm.xml was of type "integer" but in bean code it was long.
In my case getters and setter names were different from Variable name.
private Long stockId;
public Long getStockID() {
return stockId;
}
public void setStockID(Long stockID) {
this.stockId = stockID;
}
where it should be
public Long getStockId() {
return stockId;
}
public void setStockId(Long stockID) {
this.stockId = stockID;
}
In my case, I solved it changing the #Id field type from long to Long.
In my particular case, this was caused by a method in my service implementation that needed the spring #Transactional(readOnly = true) annotation. Once I added that, the issue was resolved. Unusual though, it was just a select statement.
Make sure you aren't trying to use the same User object more than once while changing the ID. In other words, if you were doing something in a batch type operation:
User user = new User(); // Using the same one over and over, won't work
List<Customer> customers = fetchCustomersFromSomeService();
for(Customer customer : customers) {
// User user = new User(); <-- This would work, you get a new one each time
user.setId(customer.getId());
user.setName(customer.getName());
saveUserToDB(user);
}
In my case, a template had a typo so instead of checking for equivalency (==) it was using an assignment equals (=).
So I changed the template logic from:
if (user1.id = user2.id) ...
to
if (user1.id == user2.id) ...
and now everything is fine. So, check your views as well!
It is a problem in your update method. Just instance new User before you save changes and you will be fine. If you use mapping between DTO and Entity class, than do this before mapping.
I had this error also. I had User Object, trying to change his Location, Location was FK in User table. I solved this problem with
#Transactional
public void update(User input) throws Exception {
User userDB = userRepository.findById(input.getUserId()).orElse(null);
userDB.setLocation(new Location());
userMapper.updateEntityFromDto(input, userDB);
User user= userRepository.save(userDB);
}
Also ran into this error message, but the root cause was of a different flavor from those referenced in the other answers here.
Generic answer:
Make sure that once hibernate loads an entity, no code changes the primary key value in that object in any way. When hibernate flushes all changes back to the database, it throws this exception because the primary key changed. If you don't do it explicitly, look for places where this may happen unintentionally, perhaps on related entities that only have LAZY loading configured.
In my case, I am using a mapping framework (MapStruct) to update an entity. In the process, also other referenced entities were being updates as mapping frameworks tend to do that by default. I was later replacing the original entity with new one (in DB terms, changed the value of the foreign key to reference a different row in the related table), the primary key of the previously-referenced entity was already updated, and hibernate attempted to persist this update on flush.
I was facing this issue, too.
The target table is a relation table, wiring two IDs from different tables. I have a UNIQUE constraint on the value combination, replacing the PK.
When updating one of the values of a tuple, this error occured.
This is how the table looks like (MySQL):
CREATE TABLE my_relation_table (
mrt_left_id BIGINT NOT NULL,
mrt_right_id BIGINT NOT NULL,
UNIQUE KEY uix_my_relation_table (mrt_left_id, mrt_right_id),
FOREIGN KEY (mrt_left_id)
REFERENCES left_table(lef_id),
FOREIGN KEY (mrt_right_id)
REFERENCES right_table(rig_id)
);
The Entity class for the RelationWithUnique entity looks basically like this:
#Entity
#IdClass(RelationWithUnique.class)
#Table(name = "my_relation_table")
public class RelationWithUnique implements Serializable {
...
#Id
#ManyToOne
#JoinColumn(name = "mrt_left_id", referencedColumnName = "left_table.lef_id")
private LeftTableEntity leftId;
#Id
#ManyToOne
#JoinColumn(name = "mrt_right_id", referencedColumnName = "right_table.rig_id")
private RightTableEntity rightId;
...
I fixed it by
// usually, we need to detach the object as we are updating the PK
// (rightId being part of the UNIQUE constraint) => PK
// but this would produce a duplicate entry,
// therefore, we simply delete the old tuple and add the new one
final RelationWithUnique newRelation = new RelationWithUnique();
newRelation.setLeftId(oldRelation.getLeftId());
newRelation.setRightId(rightId); // here, the value is updated actually
entityManager.remove(oldRelation);
entityManager.persist(newRelation);
Thanks a lot for the hint of the PK, I just missed it.
Problem can be also in different types of object's PK ("User" in your case) and type you ask hibernate to get session.get(type, id);.
In my case error was identifier of an instance of <skipped> was altered from 16 to 32.
Object's PK type was Integer, hibernate was asked for Long type.
In my case it was because the property was long on object but int in the mapping xml, this exception should be clearer
If you are using Spring MVC or Spring Boot try to avoid:
#ModelAttribute("user") in one controoler, and in other controller
model.addAttribute("user", userRepository.findOne(someId);
This situation can produce such error.
This is an old question, but I'm going to add the fix for my particular issue (Spring Boot, JPA using Hibernate, SQL Server 2014) since it doesn't exactly match the other answers included here:
I had a foreign key, e.g. my_id = '12345', but the value in the referenced column was my_id = '12345 '. It had an extra space at the end which hibernate didn't like. I removed the space, fixed the part of my code that was allowing this extra space, and everything works fine.
Faced the same Issue.
I had an assosciation between 2 beans. In bean A I had defined the variable type as Integer and in bean B I had defined the same variable as Long.
I changed both of them to Integer. This solved my issue.
I solve this by instancing a new instance of depending Object. For an example
instanceA.setInstanceB(new InstanceB());
instanceA.setInstanceB(YOUR NEW VALUE);
In my case I had a primary key in the database that had an accent, but in other table its foreign key didn't have. For some reason, MySQL allowed this.
It looks like you have changed identifier of an instance
of org.cometd.hibernate.User object menaged by JPA entity context.
In this case create the new User entity object with appropriate id. And set it instead of the original User object.
Did you using multiple Transaction managers from the same service class.
Like, if your project has two or more transaction configurations.
If true,
then at first separate them.
I got the issue when i tried fetching an existing DB entity, modified few fields and executed
session.save(entity)
instead of
session.merge(entity)
Since it is existing in the DB, when we should merge() instead of save()
you may be modified primary key of fetched entity and then trying to save with a same transaction to create new record from existing.

How to catch EntityNotFoundException with getOne() method in Spring Data JPA?

I need to know how can I catch the EntityNotFoundException with the getOne() method.
I know you will suggest me to go with a method like findById() or findOne() which are retrieving the real entity rather than a proxy Object.
But in my case, I have two foreign keys to set before inserting the object. So to set those keys If I use a method starting with "find*" you can clearly see there will be redundant database calls to fetch those entities.
But according to my knowledge with the aid of this proxy object which was return from the getOne() method I can perform this operation with a single insert query.
So now the problem is in a case of an invalid foreign key passed to the getOne() method I need to send an error response to the end user by catching the EntityNotFoundException that the inserted foreign key is not valid.
How can I achieve this?
If the insert is failed because of the invalid foreign key , hibernate will throw org.hibernate.exception.ConstraintViolationException which internally has the name of the DB constraint that causes this exception. You can then use it to determine which foreign key causes it.
Please note that Spring may wrap this exception with it own Exception object due to its persistence exception translation feature. That means after you catch the exception thrown by spring-data , you may have to traverse the causal chain of the exception to find out ConstraintViolationException . I normally use Guava for doing it . Something likes:
try{
repository.saveAndFlush(employee);
} catch (Exception ex){
Set<String> violateConstraintNames= Throwables.getCausalChain(ex).stream()
.filter(e -> e instanceof ConstraintViolationException)
.map(e -> (ConstraintViolationException) e)
.map(ConstraintViolationException::getConstraintName)
.collect(toSet());
if(violateConstraintNames.contain("employee_department_fk")){
throw new RuntimeException("Department does not exist");
}else if (violateConstraintNames.contain("employee_manager_fk")){
throw new RuntimeException("Manager does not exist");
}else{
throw ex;
}
}
The code looks ugly to me when comparing to using findById() to get and check if the referenced objects are valid or not. It also leaks the DB implementation details (i.e name of the foreign key) to the codes which is something that I would avoid. You are right that it will introduce additional select SQL, but I would consider it as premature optimisation as select by ID should be very fast thanks to the database index. Also in the non-trivial application, it is very common that you will sooner or later find that you have to get those referenced objects for checking if it pass some business rules or not.

Is it possible to store null value of an enumerated attribute to database?

I am trying to store null value of an enumerated attribute of an entity in Java to Oracle database using JPA TopLink. The code is executed with no exception or warning, but the entity is not stored in the database.
The entity is defined as follows:
#Entity
public class LetterDoc {
...
#Column(name = "delivery_type", columnDefinition = "VARCHAR2(20)", nullable = true)
#Enumerated(EnumType.STRING)
private DocDeliveryTypeEnum deliveryType;
...
}
The Enum class DocDeliveryTypeEnum have only required values.
public enum DocDeliveryTypeEnum {
NORMAL,
RECOMMENDED,
ACKNOWLEDGEMENT
}
In my situation, I have instances of entities where the deliveryType makes sence and is required. However, for some cases, the deliveryType is irrelevant and I want to leave it null. I don't want to set a default value as there is no adequate value for this case.
The entity instance has the value set to null, but when I try to persist and flush the instance in the database, it is just not stored, although the column is defined as VARCHAR2(20) with NULL value enabled.
The situation is something like this:
LetterDoc let = new LetterDoc();
// settery ostatnich atributu
let.setDeliveryType(null);
try {
emptyDocDAO.persist(let);
emptyDocDAO.flush();
} catch (Exception e) {
// no exception is really catched :(
log.error(e);
}
// here I have the id of the entity
Really no exception is thrown while persisting, just the entity is not stored. When I try to get the entity later in another thread by the ID, it fails with NoResultException.
Is there any chance to make this work, or do I really have to set a value for all enumerated attributes before persisting? Do I really have to create a default value in the Enum class?
I have solved my problem by setting a default value that does not make much sense in the particular situation. However, I wonder if there is any other option.
Thank you very much for your experience and advice.
Yes, you can store null for a enum attribute.
I can't see how you could just get nothing, is an exception occurring that your are not catching?
Enable logging on finest and include the log and your code, and catch any exceptions and include the stack trace.
The code should throw NullPointerException as you are trying to save null value to enum.
The Enum.valueOf() method throws NullPointerException if name is null.
let.setDeliveryType(null);

hibernate column uniqueness question

I'm still in the process of learning hibernate/hql and I have a question that's half best practices question/half sanity check.
Let's say I have a class A:
#Entity
public class A
{
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique=true)
private String name = "";
//getters, setters, etc. omitted for brevity
}
I want to enforce that every instance of A that gets saved has a unique name (hence the #Column annotation), but I also want to be able to handle the case where there's already an A instance saved that has that name. I see two ways of doing this:
1) I can catch the org.hibernate.exception.ConstraintViolationException that could be thrown during the session.saveOrUpdate() call and try to handle it.
2) I can query for existing instances of A that already have that name in the DAO before calling session.saveOrUpdate().
Right now I'm leaning towards approach 2, because in approach 1 I don't know how to programmatically figure out which constraint was violated (there are a couple of other unique members in A). Right now my DAO.save() code looks roughly like this:
public void save(A a) throws DataAccessException, NonUniqueNameException
{
Session session = sessionFactory.getCurrentSession();
try
{
session.beginTransaction();
Query query = null;
//if id isn't null, make sure we don't count this object as a duplicate
if(obj.getId() == null)
{
query = session.createQuery("select count(a) from A a where a.name = :name").setParameter("name", obj.getName());
}
else
{
query = session.createQuery("select count(a) from A a where a.name = :name " +
"and a.id != :id").setParameter("name", obj.getName()).setParameter("name", obj.getName());
}
Long numNameDuplicates = (Long)query.uniqueResult();
if(numNameDuplicates > 0)
throw new NonUniqueNameException();
session.saveOrUpdate(a);
session.getTransaction().commit();
}
catch(RuntimeException e)
{
session.getTransaction().rollback();
throw new DataAccessException(e); //my own class
}
}
Am I going about this in the right way? Can hibernate tell me programmatically (i.e. not as an error string) which value is violating the uniqueness constraint? By separating the query from the commit, am I inviting thread-safety errors, or am I safe? How is this usually done?
Thanks!
I think that your second approach is best.
To be able to catch the ConstraintViolation exception with any certainty that this particular object caused it, you would need to flush the session immediately after the call to saveOrUpdate. This could introduce performance problems if you need to insert a number of these objects at a time.
Even though you would be testing if the name already exists in the table on every save action, this would still be faster than flushing after every insert. (You could always benchmark to confirm.)
This also allows you to structure your code in such a way that you could call a 'validator' from a different layer. For example, if this unique property is the email of a new user, from the web interface you can call the validation method to determine if the email address is acceptable. If you went with the first option, you would only know if the email was acceptable after trying to insert it.
Approach 1 would be ok if:
There is only one constraint in the entity.
There is only one dirty object in the session.
Remember that the object may not be saved until flush() is called or the transaction commited.
For best error reporting I would:
Use approach two for every constraint violation, so I can give an specific error for each of them..
Implement an interceptor that in case of an constraint exception retries the transaction (a max number of times) so the violation can't be caught in one of the tests. This is only needed depending on the transaction isolation level.

Categories