views:

49

answers:

2

Ok, I'm trying to setup an application in a Java EE Container. I use JPA for persistence and I also use the javax.validation.constraints.* constraints. By default the container validate the entities during the @PrePersist and @PreUpdate lifecycle events and it's good for me, but how do I handle the ConstraintViolationException?

I can't find any docs on it, any suggestion is welcomed.

+1  A: 

Well, you could catch it :) Here is an example (from a unit test):

public class CustomerTest {
    private static EntityManagerFactory emf;
    private EntityManager em;

    @BeforeClass
    public static void createEntityManagerFactory() {
        emf = Persistence.createEntityManagerFactory("MyPu");
    }

    @AfterClass
    public static void closeEntityManagerFactory() {
        emf.close();
    }

    @Before
    public void beginTransaction() {
        em = emf.createEntityManager();
        em.getTransaction().begin();
    }

    @After
    public void rollbackTransaction() {
        if (em.getTransaction().isActive()) {
            em.getTransaction().rollback();
        }
        if (em.isOpen()) {
            em.close();
        }
    }

    @Test
    public void nameTooShort() {
        try {
            Customer customer = new Customer("Bo");
            em.persist(customer);
            em.flush();
            fail("Expected ConstraintViolationException wasn't thrown.");
        } catch (ConstraintViolationException e) {
            assertEquals(1, e.getConstraintViolations().size());
            ConstraintViolation<?> violation = e.getConstraintViolations().iterator().next();

            assertEquals("name", violation.getPropertyPath().toString());
            assertEquals(Size.class, violation.getConstraintDescriptor().getAnnotation().annotationType());
        }
    }
}

Where my Customer looks like:

@Entity
public class Customer {
    @Id @GeneratedValue
    @NotNull
    private Long id;

    @NotNull
    @Size(min = 3, max = 80)
    private String name;

    private boolean archived;

    ...
}

But this was just an example to show a tiny part of the API.

In my opinion, you should actually handle the validation at the view level. Many presentation frameworks support Bean Validation: JSF 2.0, Wicket, Spring MVC...

See also

Pascal Thivent
I tryed to do something like you suggest in the first code sample, in an EJB, but it don't work, it don't catch the exception, maybe I could post some cose. I also thought the same thing about the presentation layer validation, but I'd like to do it at best.
s.susini
@Pascal: If you are in a transaction, `ConstraintViolation` can happen _ANYTIME_ (more precisely, at FlushTime) inside the transaction -- happens typically when you read after you write in the TRA. So your code will **NOT** work.
pihentagy
`assert` s in the catch block without explicitly requiring the `ConstraintViolationException` anywhere, seems to be a bad idea IMHO.
pihentagy
@pihentagy: First, as I wrote, this was an example taken from a unit test to show the API. Second, this code **DOES** work in the test it was taken from (my tests methods are running inside a transaction). Third, there is a `fail()` in my test, the test will fail if the exception is not thrown. So thank you for your criticisms but they make no sense for me and if you have a better answer, feel free to post it.
Pascal Thivent
@Pascal: Sorry, missed the `fail()` line. But my other comment still holds: `ConstraintViolationException` can happen anytime between your "insert/update" and your `flush()` / `commit()`. Ok, in your answer you `flush()` explicitly, but flushing after each insert is ineffective, not speaking about implicit updates (when you `find()`, and then set some properties)
pihentagy
@pihentagy: No, they don't, at least not in the context of my answer. My goal is clearly to test the **content** of the `ConstraintViolationException` and my test is just doing what it is intended for (it isn't written to be the most effective, it's written to test a specific behavior). So please don't come with constraints that don't apply to justify an irrelevant "know-it-all" comment claiming that my code doesn't work. Thanks, I'm done now.
Pascal Thivent
@Pascal: Ok, well done, you can catch `ConstraintViolationException`. But in production, the exception will be thrown, where you wouldn't expect it (unless you flush). Since I consider this as a serious Gotcha, I thought it worth to share... Nothing personal, really.
pihentagy
A: 

You can catch ConstraintViolationException, as Pascal described it, but please be aware, that this Exception could be thrown anytime after your create/update statement, and before the end of the transaction (see FlushModeType). Typical throwing points are when you read something after your create/update statement, or at an explicit flush() statement (which you should use with caution).

pihentagy