Is it possible with JPA to automatically nullify children when deleting the parent?
I have a Model Class Device, with sync statistics attached to it. When I remove the device I still want to keep the statistics, so put the field device in the Statistics class to 'null'.
#OneToMany (mappedBy = "device", fetch= FetchType.LAZY)
public List<Statistic> syncStats;
and
#ManyToOne (fetch= FetchType.LAZY)
public Device device;
Is there an automatic way of obtaining this effect or do I have to do it manually?
Currently I use:
#PreRemove
protected void removeLinkToStats() {
syncStats.clear();
}
but this still gives a "A javax.persistence.PersistenceException has been caught, org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update"
Is it possible with JPA to automatically nullify children when deleting the parent?
No. With JPA it is the application's responsibility to keep sync. Your application should call business logic before removing the entity using JPA.
Of course there are multiple ways of doing so. Using annotations like PreRemove or PreDestroy or (my personal favorite) do your pre removal things in a business container wich calls your data access to remove the entity from the database.
Short example:
class Business
{
DataAccess dataAccess;
//.....
public void removeEntity(Entity e)
{
//do pre removal here
dataAccess.remove(e);
}
//......
}
class DataAccess
{
EntityManager em;
//......
public void remove(Entity e)
{
em.remove(e);
}
//....
}
Related
In a DDD-project I'm contributing to, we're seeking for some convenient solutions to map entity objects to domain objects and visa versa.
Developers of this project agreed to fully decouple domain model from data model.
The data layer uses JPA (Hibernate) as persistence technology.
As we all reckon that persistence is an implementation detail in DDD, from a developers' point of view we're all seeking for the most appropriate solution in every aspect of the application.
The biggest concern we're having is when an aggregate, containing a list of entities, is mapped to a JPA entity that in it's turn contains a one-to-many relationship.
Take a look at the example below:
Domain model
public class Product extends Aggregate {
private ProductId productId;
private Set<ProductBacklogItem> backlogItems;
// constructor & methods omitted for brevity
}
public class ProductBacklogItem extends DomainEntity {
private BacklogItemId backlogItemId;
private int ordering;
private ProductId productId;
// constructor & methods omitted for brevity
}
Data model
public class ProductJpaEntity {
private String productId;
#OneToMany
private Set<ProductBacklogItemJpaEntity> backlogItems;
// constructor & methods omitted for brevity
}
public class ProductBacklogItemJpaEntity {
private String backlogItemId;
private int ordering;
private String productId;
// constructor & methods omitted for brevity
}
Repository
public interface ProductRepository {
Product findBy(ProductId productId);
void save(Product product);
}
class ProductJpaRepository implements ProductRepository {
#Override
public Product findBy(ProductId productId) {
ProductJpaEntity entity = // lookup entity by productId
ProductBacklogItemJpaEntity backlogItemEntities = entity.getBacklogItemEntities();
Set<ProductBacklogItem> backlogItems = toBackLogItems(backlogItemEntities);
return new Product(new ProductId(entity.getProductId()), backlogItems);
}
#Override
public void save(Product product) {
ProductJpaEntity entity = // lookup entity by productId
if (entity == null) {
// map Product and ProductBacklogItems to their corresponding entities and save
return;
}
Set<ProductBacklogItem> backlogItems = product.getProductBacklogItems();
// how do we know which backlogItems are: new, deleted or adapted...?
}
}
When a ProductJpaEntity already exists in DB, we need to update everything.
In case of an update, ProductJpaEntity is already available in Hibernate PersistenceContext.
However, we need to figure out which ProductBacklogItems are changed.
More specifically:
ProductBacklogItem could have been added to the Collection
ProductBacklogItem could have been removed from the Collection
Each ProductBacklogItemJpaEntity has a Primary Key pointing to the ProductJpaEntity.
It seems that the only way to detect new or removed ProductBacklogItems is to match them by Primary Key.
However, primary keys don't belong in the domain model...
There's also the possibility to first remove all ProductBacklogItemJpaEntity instances (which are present in DB) of a ProductJpaEntity, flush to DB, create new ProductBacklogItemJpaEntity instances and save them to DB.
This would be a bad solution. Every save of a Product would lead to several delete and insert statements in DB.
Which solution exists to solve this problem without making too many sacrifices on Domain & Data model?
You can let JPA/Hibernate solve problem for you.
public void save(Product product) {
ProductJpaEntity entity = convertToJpa(product);
entityManager.merge(entity);
// I think that actually save(entity) would call merge for you,
// if it notices that this entity already exists in database
}
What this will do is:
It will take your newly created JPA Entity and attach it
It will examine what is in database and update all relations accordingly, with priority given to your created entity (if mappings are set correctly)
This is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
Entity views can also be updatable and/or creatable i.e. support flushing changes back, which can be used as a basis for a DDD design.
Updatable entity views implement dirty state tracking. You can introspect the actual changes or flush changed values.
You can define your updatable entity views as abstract classes to hide "implementation specifics" like e.g. the primary key behind the protected modifier like this:
#UpdatableEntityView
#EntityView(ProductJpaEntity.class)
public abstract class Product extends Aggregate {
#IdMapping
protected abstract ProductId getProductId();
public abstract Set<ProductBacklogItem> getBacklogItems();
}
#UpdatableEntityView
#EntityView(ProductBacklogItemJpaEntity.class)
public abstract class ProductBacklogItem extends DomainEntity {
#IdMapping
protected abstract BacklogItemId getBacklogItemId();
protected abstract ProductId getProductId();
public abstract int getOrdering();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
Product p = entityViewManager.find(entityManager, Product.class, id);
Saving i.e. flushing changes is easy as well
entityViewManager.save(entityManager, product);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features and for flushing changes, you can define a save method in your repository that accepts the updatable entity view
I believe you need to address the issue in a different way.
It is really hard to determine which has been changed when you have a complex graph of objects. However, there should be someone else (maybe a service) which really knows what have changed in advance.
In fact, I did not see in your question the real business "Service" or a class which address the business logic. This will be the one who can solve this issue. As a result, you will have in your repository something more specific removeProductBacklogItem(BacklogItemId idToRemove) or... addProductBacklogItem(ProductId toProductId, ProductBacklogItem itemToAdd). That will force you to manage and identify changes in other way... and the service will be responsible for.
I try to create a log entry as soon as one of my entities got changed or created. In order to do this, I registered an EntityListener on an AbstractEntity class. AbstractEntity has a List of LogEntries and the cascade type of this list is ALL (all of my entities inherits from AbstractEntity).
Current implementation of my EntityListener:
public class EntityChangeListener {
#Inject
SessionController sessionController;
#PreUpdate
public void preUpdate(AbstractEntity entity) {
createLogEntryFor(entity, LogEntry.ChangeType.UPDATED);
}
#PrePersist
public void prePersist(AbstractEntity entity) {
createLogEntryFor(entity, LogEntry.ChangeType.CREATED);
}
private void createLogEntryFor(AbstractEntity entity, LogEntry.ChangeType changeType) {
if (!(entity instanceof LogEntry)) {
Date now = Calendar.getInstance().getTime();
LogEntry logEntry = new LogEntry();
logEntry.setCreator(sessionController.getCurrentUser());
logEntry.setAbstractEntity(entity);
logEntry.setChangeDate(now);
logEntry.setChangeType(changeType);
entity.getLogEntries().add(logEntry);
}
}
}
The problem is that the log entries are not persisted, although using cascade type all. I also tried to remove the cascade type and inject my LogEntryService (SLSB with CRUD methods) in order to persist the LogEntry manually, but it has no effect as well.
Same problem occurs by using #PostPersist and #PostUpdate.
JPA provider is EclipseLink (2.5.0).
Switching to Hibernate and using Envers is no option.
The prePersist event should work, as prePersist is called before changes are computed.
For preUpdate this will not work as the changes are computed before the preUpdate event is called, so it is too late to change anything further.
You can use the EclipseLink DescriptorEvents instead, as the give you access to more advanced options. You can get the Session and call insertObject() on it directly to force the insertion of the log entry, or change the object or UnitOfWork ChangeSet.
Also consider EclipseLink's history support,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/History
EclipseLink should provide an option to do a two pass commit, to allow events to change objects, please log a bug for this and vote for it (or find and vote for an existing one).
I am a bit confused about managing relationship in JPA.
basically I have two entities with a One to Many relationship
A configuration can have have a one or many email list associated with it.
#Entity
public class Config {
#OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<Email> emailReceivers;
}
#Entity
public class Email {
#ManyToOne
private Config owner;
}
In an EJB and during update/merge operation wherein I would edit the list of emails associated with a configuration,
I thought that I dont need to explicitly call the delete operation on my email Entity and I would just manage the relationship by deleting the email in my configuration email list.
#Stateless
public class ConfigFacadeImpl implements ConfigFacade{
#EJB
private ConfigDao configDao;
#EJB
private EmailDao emailDao;
#Override
public void update(Config Config, List<Email> emailsForDelete) {
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
config.getEmailReceivers().remove(email);
}
}
configDao.update(config);
}
}
If I don't execute the delete and only remove it from the list, it wont erase my table row.
The UI and the database is now not in sync as the UI would not show the email(s) that I have deleted but when you check the database, the row(s) are still there.
Is it required? I thought JPA would handle this for me if I would just remove it in my entities.
UPDATE
I have tweaked my code to get the entity from the database first before making any changes but still it is not deleting my child email entities. I wonder if this is an apache derby issues. (This is the correct way right as I am passing my entities from my JSF managed bean into my EJB so I need to get the sync from the DB first.)
#Override
public void update(Config config, List<Email> emailsForDelete) {
Config configTemp = configDao.find(config.getId());
if(emailsForDelete!=null && emailsForDelete.size() > 0){
for(Email emailTemp: emailsForDelete){
configTemp.getEmailReceivers().remove(emailTemp);
}
}
configDao.update(config);
}
Since you have already defined cascade type = CascadeType.ALL, JPA should take care of the deletion. Explicit Delete statement is not required.
These two statements are not required:
Email email = emailDao.find(emailTemp.getId());
emailDao.delete(email); // Do I need to explicitly call the remove??
Instead, you may want to just find the matching emailReceiver in config.getEmailReceivers() and remove the matching EmailReceivers as you are doing. There is no need to load the Email entity from the database.
EDIT: To delete orphan objects, you may want to include CascadeType.DELETE_ORPHAN cascade attribute along with CascadeType.ALL.
This is the same issue as in Why merging is not cascaded on a one to many relationship
Basically, JPA can only cascade over entities in your collection. So changes to child objects removed from the collection are never putinto the context, and so can't be pushed to the database. In this case, the oneToMany is controlled by the manytones back pointer, so even collection changes won't show up unless the child is also merged. Once a child is pruned from the tree, it needs to be managed and merged individually for changes to it to be picked up.
With JPA 2.0, you can use the option orphanRemoval=true in parent entity
Example:
#Entity
public class Parent {
...
#OneToMany(mappedBy="parentId",cascade=CascadeType.ALL, orphanRemoval=true)
private List<Child> childList;
...
}
I am writing an application that has typical two entities: User and UserGroup. The latter may contain one or more instances of the former. I have following (more/less) mapping for that:
User:
public class User {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = {CascadeType.MERGE})
#JoinColumn(name="GROUP_ID")
private UserGroup group;
public UserGroup getGroup() {
return group;
}
public void setGroup(UserGroup group) {
this.group = group;
}
}
User group:
public class UserGroup {
#Id
#GeneratedValue
private long id;
#OneToMany(mappedBy="group", cascade = {CascadeType.REMOVE}, targetEntity = User.class)
private Set<User> users;
public void setUsers(Set<User> users) {
this.users = users;
}
}
Now I have a separate DAO class for each of these entities (UserDao and UserGroupDao). All my DAOs have EntityManager injected using #PersistenceContext annotation, like this:
#Transactional
public class SomeDao<T> {
private Class<T> persistentClass;
#PersistenceContext
private EntityManager em;
public T findById(long id) {
return em.find(persistentClass, id);
}
public void save(T entity) {
em.persist(entity);
}
}
With this layout I want to create a new user and assign it to existing user group. I do it like this:
UserGroup ug = userGroupDao.findById(1);
User u = new User();
u.setName("john");
u.setGroup(ug);
userDao.save(u);
Unfortunately I get following exception:
object references an unsaved transient instance - save the transient
instance before flushing: x.y.z.model.User.group ->
x.y.z.model.UserGroup
I investigated it and I think it happens becasue each DAO instance has different entityManager assigned (I checked that - the references in each DAO to entity manager are different) and for user entityManager does not manager the passed UserGroup instance.
I've tried to merge the user group assigned to user into UserDAO's entity manager. There are two problems with that:
It still doesn't work - the entity manager wants to overwrite the existing UserGroup and it gets exception (obviously)
even if it worked I would end up writing merge code for each related entity
Described case works when both find and persist are made using the same entity manager. This points to a question(s):
Is my design broken? I think it is pretty similar to recommended in this answer. Should there be single EntityManager for all DAOs (the web claims otherwise)?
Or should the group assignment be done inside the DAO? in this case I would end up writing a lot of code in the DAOs
Should I get rid of DAOs? If yes, how to handle data access nicely?
any other solution?
I am using Spring as container and Hibernate as JPA implementation.
Different instances of EntityManager are normal in Spring. It creates proxies that dynamically use the entity manager that is currently in a transaction if one exists. Otherwise, a new one will be created.
The problem is that your transactions are too short. Retrieving your user group executes in a transaction (because the findById method is implicitly #Transactional ). But then the transaction commits and the group is detached. When you save the new user, it will create a new transaction which fails because the user references a detached entity.
The way to solve this (and to do such things in general) is to create a method that does the whole operation in a single transaction. Just create that method in a service class (any Spring-managed component will work) and annotate it with #Transactional as well.
I don't know Spring, but the JPA issue is that you are persisting a User that has a reference to a UserGroup, but JPA thinks the UserGroup is transient.
transient is one of the life-cycle states a JPA entity can be in. It means it's just created with the new operator, but has not been persisted yet (does not have a persistent identity yet).
Since you obtain your UserGroup instance via a DAO, it seems like something is wrong there. Your instance should not be transient, but detached. Can you print the Id of the UserGroup instance just after your received it from the DAO? And perhaps also show the findById implementation?
You don't have cascade persist on the group relation, so this normally should just work if the entity was indeed detached. Without a new entity, JPA simply has no way to set the FK correctly, since it would need the Id of the UserGroup instance here but that (seemingly) doesn't exist.
A merge should also not "overwrite" your detached entity. What is the exception that you're getting here?
I only partially agree with the answers being given by the others here about having to put everything in one transaction. Yes, this indeed may be more convenient as the UserGroup instance will still be 'attached', but it should not be -necessary-. JPA is perfectly capable of persisting new entities with references to either other new entities or existing (detached) entities that were obtained in another transaction. See e.g. JPA cascade persist and references to detached entities throws PersistentObjectException. Why?
I am not sure how but I've managed to solve this. The user group I was trying to assign the user to had NULL version field in database (the field annotated with #Version). I figured out it was an issue when I was testing GWT RequestFactory that was using this table. When I set the field to 1 everything started to work (no changes in transaction handling were needed).
If the NULL version field really caused the problem then this would be one of the most misleading exception messages I have ever got.
Currently I have a child entity that has a #ManyToOne association to it's parent entity. Previous developers have set this field as lazy="false" to get the parent whenever needed when the session is closed too, however I decided it should be lazy="true" as it's not always used but when doing so I ran into LazyInitializationException because the session is closed and the child is detached from the session when it tries to get the parent.
I was wondering if it's right to move some more logic of the run method as seen bellow to the service class which interacts with DAOs thus I could avoid the exception because currently the service classes are like plain classes which have the needed DAOs injected and they just call the DAO method and returns the result.
Should I put like more methods in the service class which interact with the entities, which would get the user and check everything for log in action, get parent if needed and then just return the log in result to run method..
public class Login extends Runnable {
private UserService userService;
...
public void run() {
...
User user = userSerivce.getById(id);
Account account = user.getAccount(); //LazyInitializationException
...
if (account.isLocked()) {
...
}
...
userService.save(user);
//Send some message to the user..
}
}
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
...
public User getById(long id) {
return userDAO.getById(id);
}
public void save(User user) {
userDAO.save(user);
}
}
public UserDAOImpl implements UserDAO {
private SessionFactory factory;
...
public User getById(long id) {
return (User) factory.getCurrentSession().load(User.class, id);
}
public void save(User user) {
factory.getCurrentSession().saveOrUpdate(user);
}
}
I use Spring's <tx:advice> to handle the closing and other transaction related stuff.
I prefer to have all of my entity relationships as lazy since I don't know if and when I'll need those external entities. This way I can avoid unnecessary joins when I don't need the additional entities. If I do end up needing the entity I create a named query and eager fetch the entity or collection. Here's an example.
I do agree though that you should be sending a DTO instead of the entity back to your calling front end application. Hibernate entities are full of proxies and it would be inefficient to send them back. I'm not really sure if you are sending these objects to a jsp/velocity/etc file or to an external application but I would recommend using a DTO if you are sending back JSON or something similar to the calling application. Here's another question that deals with DTO's click here that discusses 2 frameworks for easy conversion.
Create DTOs, don't send JPA Entities over the net.
When you create the DTOs, you will have to access the required properties, what will trigger to load them.