Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unique constraint with JPA and Bean Validation

I'd like to have a @Unique constraint with Bean Validation, but that is not provided by the standard. If I would use JPA's @UniqueConstraint I wouldn't have a unique validation and error reporting mechanism.

Is there a way to define @Unique as a Bean Validation constraint and combine it with JPA, such that JPA creates a column with an unique constraint and checks wheter a value is unique or not?

like image 301
deamon Avatar asked Aug 16 '10 16:08

deamon


People also ask

What is unique constraint in JPA?

A unique constraint can be either a column constraint or a table constraint. At the table level, we can define unique constraints across multiple columns. JPA allows us to define unique constraints in our code using @Column(unique=true) and @UniqueConstraint.

What is Bean Validation constraints?

The Bean Validation model is supported by constraints in the form of annotations placed on a field, method, or class of a JavaBeans component, such as a managed bean. Constraints can be built in or user defined. User-defined constraints are called custom constraints.

How do you handle unique constraint exception in Java?

To handle unique constraint violations: Catch uniqueness exceptions thrown by the database at the lowest level possible — in the UnitOfWork class. Convert them into Result. Use the UnitOfWork in the controller to explicitly commit pending changes and see if there are any uniqueness constraint violations.

Can I define multiple unique constraints on a table?

PRIMARY KEY constraint differs from the UNIQUE constraint in that; you can create multiple UNIQUE constraints in a table, with the ability to define only one SQL PRIMARY KEY per each table.


4 Answers

Unless you acquire a lock on a whole table, it is basically not possible to check for unicity using a SQL query (any concurrent transaction could modify data after a manual check but before the commit of the ongoing transaction). In other words, it isn't possible to implement a valid unique verification at the Java level and thus to provide a validation implementation. The only reliable way to check for unicity is while committing the transaction.

The BV spec summarizes it like this:

Appendix D. Java Persistence 2.0 integration

Question: should we add @Unique that would map to @Column(unique=true)?

@Unique cannot be tested at the Java level reliably but could generate a database unique constraint generation. @Unique is not part of the BV spec today.

So while I agree that it would be nice to have unique (and non null) constraint violations wrapped in a Bean Validation exception, this is currently not the case.

References

  • Bean Validation specification (JSR 303)
    • Appendix D. Java Persistence 2.0 integration
  • Question about validation and persistence constraints
like image 66
Pascal Thivent Avatar answered Sep 23 '22 15:09

Pascal Thivent


More information on how to implement a @Unique and the problematic around it can be found here - http://community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator

like image 28
Hardy Avatar answered Sep 23 '22 15:09

Hardy


Well you CAN do it, but it's not trivial. The problem is: the validator requires database access to perform some queries to check, if the value you want to insert is already there or not. And this can't be really done from the validator, as it doesn't have access to the sessionFactory/session. Of course you could instantiate it (session/sessionFactory) inside the validator, but it's not a good coding practice.

like image 25
Mateusz Dymczyk Avatar answered Sep 22 '22 15:09

Mateusz Dymczyk


You can make a validator read the JPA annotations and apply it. Here is somewhat of an example using spring validators that can be used as an idea to expand on.

JPA JSR303 Spring Form Validation

You can also inject (@Inject or Spring's @Autowired) your session bean in a custom validator and the container should know how to wire it up. I only know this as a Spring example:

import javax.validation.ConstraintValidator;

public class MyConstraintValidator implements ConstraintValidator {

@Autowired //@Inject
private Foo aDependency;

...
}
like image 41
dukethrash Avatar answered Sep 19 '22 15:09

dukethrash