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.
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
6.3. Presentation layer validation
Spring MVC, Spring Bean Validation Framework and validating confirm password / confirm email fields.
Wicket JSR-303 Validators
TOTD #123: f:ajax, Bean Validation for JSF, CDI for JSF and JPA 2.0 Criteria API
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).
Related
I'm trying to write integration test for Spring Boot application. I have Product and GalleryImage domain model. They are in one-to-many relationship.
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
I have a integration test as below:
#Test
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}
However, this test gives me a LazyInitializationException. I searched on Google and StackOverFlow, it says that the session is closed after productRepository.findOne(1L), since galleryImages are lazily loaded, so galleryImages.size() gives me this exception.
I have tried to add a #Transactional annotation on the test, but it's still not working.
Hibernate Session has been closed after following line productRepository.findOne(1L).
You can try to do Hibernate.initialize(product.getGalleryImages())
public static void initialize(Object proxy)
throws HibernateException
Force initialization of a proxy or persistent collection.
Note: This only ensures intialization of a proxy object or collection; it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
To avoid Hibernate.initialize you can create a service.
#Service
#Transactional
public class ProductService {
#Transactional(readOnly = true)
public List<GalleryImage> getImages(final long producId) throws Exception {
Product product = productRepository.findOne(producId);
return product.getGalleryImages();
}
}
If you do use Spring Data JPA in you application then dynamic finder is a good alternative.
I had similar issues in the past and the solution was indeed adding the #Transactional annotation, but contrarily to what is proposed by #Anton M answer I believe that in this case we should annotate the test instead as proposed here.
It works both ways but the annotation should be placed if and where necessary, i.e. add it on the service if you need it there and not just for testing purposes.
For example, keep this as it was:
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
And add the annotation here:
#Test
#Transactional
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}
I have a class, Student and the generated Endpoint class for it. ListStudents and insertStudents methods work without any problems, but update and remove don't cause any change in the datastore. The methods don't throw any errors and the call returns, but no changes are made.
My endpoints code is mostly the code generated by google plugin for eclipse:
#ApiMethod(name = "removeStudent", path="remove_student")
public void removeStudent(#Named("email") String email) {
EntityManager mgr = getEntityManager();
try {
Student student = getStudentByEmailName(email);
mgr.remove(student);
} finally {
mgr.close();
}
}
Entitiy manager getter method:
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
#ApiMethod(name = "updateStudent")
public Student updateStudent(Student student) {
EntityManager mgr = getEntityManager();
try {
if (!containsStudent(student)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(student);
} finally {
mgr.close();
}
return student;
}
And my EMF class:
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence
.createEntityManagerFactory("transactions-optional");
private EMF() {
}
public static EntityManagerFactory get() {
return emfInstance;
}
}
The client that uses this endpoint is Android. I have only tried testing on my local server.
Please tell me if I'm doing something wrong. Thank you
Do you have your student entities indexed by email?
This is a typical issue when you move to nosql and expect all queries to work without indexes.
Note that records inserted before defining index would not be in index.
The datastore is eventually consistent and your code should work. What is the return value that you get in the Student object from your updateStudent method.
As much as I don't want to, after you do a mgr.persist(...) , add mgr.flush() and see if that makes a difference.
I am moving a JPA-Hibernate application from a Java EE 6 environment to a Tomcat 7 one.
The application has several DAO classes making queries on the EntityManager. In the Java EE environment I could just inject it using the #PersistenceContext annotation, and let the container manage the EntityManager. Now that I have to do it manually, I was wondering what is the way to go.
Should the entity manager be unique? If so can it be a static final field, created on startup and that every DAO class uses? Does it have a lifecycle that involves closing it and then re-opening it?
Unfortunately, you'll have to do it manually. The way I usually do it is to define a special class:
public class EMF {
private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("name");
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}
So, every time you need EntityManager, you have to create it manually. You need to handle transactions as well:
EntityManager em = EMF.getEntityManager();
EntityTransaction et = em.getTransaction();
try {
MyEntity my = new MyEntity();
et.begin();
try {
em.persist(my);
et.commit();
} catch (Exception ex) {
if (et.isActive())
et.rollback();
}
} finally {
em.close();
}
My previous question How to wrap Wicket page rendering in a Spring / Hibernate transaction? has led me to thinking about transaction demarcation in Wicket.
Whilst the example there was easily solved by moving business logic down into a Spring-managed layer, there are other places where this is not possible.
I have a generic DAO class, implemented by Hibernate, with
public class HibernateDAO<T> implements DAO<T> {
protected final Class<T> entityClass;
private final SessionFactory sessionFactory;
#Transactional
public T load(Serializable id) {
return (T) getSession().get(entityClass, id);
}
#Transactional
public void saveOrUpdate(T object) {
getSession().saveOrUpdate(object);
}
}
and a generic model to fetch it
public class DAOEntityModel<T> extends LoadableDetachableModel<T>{
private DAO<T> dao;
private final Serializable id;
public DAOEntityModel(DAO<T> dao, Serializable id) {
this.dao = dao;
this.id = id;
}
public <U extends Entity> DAOEntityModel(DAO<T> dao, U entity) {
this(dao, entity.getId());
}
public Serializable getId() {
return id;
}
#Override
protected T load() {
return dao.load(id);
}
}
Now I have a minimal form that changes an entity
public class ScreenDetailsPanel extends Panel {
#SpringBean(name="screenDAO") private DAO<Screen> dao;
public ScreenDetailsPanel(String panelId, Long screenId) {
super(panelId);
final IModel<Screen> screenModel = new DAOEntityModel<Screen>(dao, screenId);
Form<Screen> form = new Form<Screen>("form") {
#Override protected void onSubmit() {
Screen screen = screenModel.getObject();
dao.saveOrUpdate(screen);
}};
form.add(
new TextField<String>("name", new PropertyModel<String>(screenModel, "name")));
add(form);
}
}
So far so good - thanks for sticking with it!
So my issue is this - when the form is submitted, the PropertyModel will load the screenModel, which will happen in the transaction delineated by the #Transactional dao.load(id). The commit of the changes will when the (different) transaction started for dao.saveOrUpdate(object) is committed. In between these times all bets are off, so that the object may no longer exist in the DB to be committed.
I'm never entirely sure with DB code and transactions. Should I just shrug this off as unlikely, although I could construct other more complicated but more dangerous scenarios? If not I can't see how to demarcate the whole page logic in a single transaction, which is what my instinct tells me I should be aiming for.
Typically you would solve this by putting the #Transactional annotation on a service-level class, used by your front-end layer code, which wraps around the DAO operations - so that the load and save happens within the same transaction. In other words, you can solve this by creating a layer of code between the form and the DAO code, a "service layer", which provides the business-level logic and hides the presence of DAOs from the presentation layer.
I've not yet implemented it, but I'm pretty sure that #ireddick solution in How to control JPA persistence in Wicket forms? of lazily starting a tx in in the Wicket request cycle is the best solution here. I'm going to accept this proxy for it to stop Stack Overflow nagging me to accept an answer.
Is there a best-practice pattern for completely resetting a database to a freshly-paved schema with JPA before a unit test? I have been using a testing persistence unit with hbml2ddl.auto=create-or-drop and recreating EMFs before each test, but I wonder if there's a cleaner way to do it.
Unit tests should not talk to the database.
Assuming you're writing an integration test for your data access layer, you could use a tool like DBUnit, or you could create a static test helper that programmatically resets your database state by doing all of your deletes and inserts using JPA queries inside of a transaction.
Resetting the database is not a big problem if you use a fast Java database such as the H2 database or HSQLDB. Compared to using Oracle / MySQL (or whatever you use for production) this will speed up your tests, and it will ensure all your code is tested as when using the 'real' production database.
For maximum performance, you can use H2 in-memory (that way you may not have to reset the database manually - it's reset automatically if the connection is closed), or you can use a regular persistent database. To reset the database after use in H2, run the (native) statement 'drop all objects delete files'.
DBUnit has much of what you need, I use Springs Testing framework to rollback, transactions after each test see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html
Is there a best-practice pattern for completely resetting a database to a freshly-paved schema with JPA before a unit test?
Don't reset the whole DB schema before every unit test, but reset the "DB environment (which is specific to the current unit test)" at END of each unit test.
We have an entity...
#Entity
public class Candidate implements {
private String id;
private String userName;
private EntityLifeCycle lifeCycle;
protected Candidate() {
}
public Candidate(String userName) {
this.userName = userName;
}
#Id #GeneratedValue(generator="uuid", strategy=GenerationType.AUTO)
#GenericGenerator(name="uuid", strategy="uuid", parameters = {})
#Column(name="candidate_id", nullable=false, unique=true)
public String getId() {
return id;
}
protected void setId(String id) {
this.id = id;
}
#Column(name="user_name", nullable=false, unique=true)
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#Embedded
public EntityLifeCycle getLifeCycle() {
if(lifeCycle == null) {
lifeCycle = new EntityLifeCycleImpl();
}
return lifeCycle;
}
public void setLifeCycle(EntityLifeCycleImpl lifeCycle) {
this.lifeCycle = lifeCycle;
}
#PrePersist
public void prePersist() {
lifeCycle.setCreatedDate(new Date());
}
}
We are setting the createdDate for each Candidate instance in prePersist() method. Here is a test case that asserts that createdDate is getting set properly....
public class EntityLifeCycleTest {
#Test
public void testLifeCycle() {
EntityManager manager = entityManagerFactory.createEntityManager();
Candidate bond = new Candidate("Miss. Bond");
EntityTransaction tx = manager.getTransaction();
tx.begin();
manager.persist(bond);
tx.commit();
Assert.assertNotNull(bond.getLifeCycle().getCreatedDate());
manager.close();
}
}
This test case will run properly for the first time. But if we run this test case second time it would throw ConstraintViolationException, because the userName is unique key.
Therefore, I think the right approach is to "clean the DB environment (which is specific to the current unit test)" at end of each test case. Like this...
public class EntityLifeCycleTest extends JavaPersistenceTest {
#Test
public void testLifeCycle() {
EntityManager manager = entityManagerFactory.createEntityManager();
Candidate bond = new Candidate("Miss. Bond");
EntityTransaction tx = manager.getTransaction();
tx.begin();
manager.persist(bond);
tx.commit();
Assert.assertNotNull(bond.getLifeCycle().getCreatedDate());
/* delete Candidate bond, so next time we can run this test case successfully*/
tx = manager.getTransaction();
tx.begin();
manager.remove(bond);
tx.commit();
manager.close();
}
}
I have been using a testing persistence unit with hbml2ddl.auto=create-or-drop and recreating EMFs before each test, but I wonder if there's a cleaner way to do it.
Recreating EMF before each test is time consuming, IMO.
Drop and recreate the DB schema only if you have made some changes to #Entity annotated class that affects the underlying DB (e.g. adding/removing columns and/or constraints). So first validate the schema, if the schema is valid don't recreate it, and if invalid then recreate it. Like this...
public class JavaPersistenceTest {
protected static EntityManagerFactory entityManagerFactory;
#BeforeClass
public static void setUp() throws Exception {
if(entityManagerFactory == null) {
Map<String, String> properties = new HashMap<String, String>(1);
try {
properties.put("hibernate.hbm2ddl.auto", "validate");
entityManagerFactory = Persistence.createEntityManagerFactory("default", properties);
} catch (PersistenceException e) {
e.printStackTrace();
properties.put("hibernate.hbm2ddl.auto", "create");
entityManagerFactory = Persistence.createEntityManagerFactory("default", properties);
}
}
}
}
Now, if you run all the test cases(that extend JavaPersistenceTest) in one go, the EMF will be created only once(or twice if the schema was invalid).