I have the following use case: There's a class called Template and with that class I can create instances of the ActualObject class (ActualObject copies its inital data from the Template). The Template class has a list of Product:s.
Now here comes the tricky part, the user should be able to delete Products from the database but these deletions may not affect the content of a Template. In other words, even if a Product is deleted, the Template should still have access to it. This could be solved by adding a flag "deleted" to the Product. If a Product is deleted, then it may not be searched explicitly from the database, but it can be fetched implicitly (for example via the reference in the Template class).
The idea behind this is that when an ActualObject is created from a template, the user is notified in the user interface that "The Template X had a Product Z with the parameters A, B and C, but this product has been deleted and cannot be added as such in ActualObject Z".
My problem is how I should mark these deleted objects as deleted. Before someone suggests that just update the delete flag instead of doing an actual delete query, my problem is not that simple. The delete flag and its behaviour should exist in all POJOs, not just in Product. This means I'll be getting cascade problems. For example, if I delete a Template, then the Products should also be deleted and each Product has a reference to a Price-object which also should be deleted and each Price may have a reference to a VAT-object and so forth. All these cascaded objects should be marked as deleted.
My question is how can I accomplish this in a sensible manner. Going through every object (which are being deleted) checking each field for references which should be deleted, going through their references etc is quite laborious and bugs are easy to slip in.
I'm using Hibernate, I was wondering if Hibernate would have any such inbuilt features. Another idea that I came to think of was to use hibernate interceptors to modify an actual SQL delete query to an update query (I'm not even 100% sure this is possible). My only concern is that does Hibernate rely on cascades in foreign keys, in other words, the cascaded deletes are done by the database and not by hibernate.
My problem is how I should mark these
deleted objects as deleted.
I think you have choosen a very complex way to solve the task. It would be more easy to introduce ProductTemplate. Place into this object all required properties you need. And also you need here a reference to a Product instance. Than instead of marking Product you can just delete it (and delete all other entities, such as prices). And, of course, you should clean reference in ProductTemplate. When you are creating an instance of ActualObject you will be able to notify the user with appropriate message.
I think you're trying to make things much more complicated than they should be... anyway, what you're trying to do is handling Hibernate events, take a look at Chapter 12 of Hibernate Reference, you can choose to use interceptors or the event system.
In any case... well good luck :)
public interface Deletable {
public void delete();
}
Have all your deletable objects implement this interface. In their implementations, update the deleted flag and have them call their children's delete() method also - which implies that the children must be Deletable too.
Of course, upon implementation you'll have to manually figure which children are Deletable. But this should be straightforward, at least.
If I understand what you are asking for, you add an #OneToMany relationship between the template and the product, and select your cascade rules, you will be able to delete all associated products for a given template. In your product class, you can add the "deleted" flag as you suggested. This deleted flag would be leveraged by your service/dao layer e.g. you could leverage a getProdcuts(boolean includeDeleted) type concept to determine if you should include the "deleted" records for return. In this fashion you can control what end users see, but still expose full functionality to internal business users.
The flag to delete should be a part of the Template Class itself. That way all the Objects that you create have a way to be flagged as alive or deleted. The marking of the Object to be deleted, should go higher up to the base class.
Related
Is it possible, when using Hibernate and cascade delete, to remove the cascade-deleted entity from all other entities that have a reference to it?
Take this diagram for example:
User has a MembershipOffer to join a Group. In my model is possible to remove groups with pending offers, and when that happens, I want all the offers of the deleted group to be deleted as well.
I understand that in order to do that, I will need to change the MembershipOffer-Group association to be bidirectional and set the cascade flag on it. However, when MembershipOffer is deleted, what happens to the User's reference to it?
I assume Hibernate won't go through every reference to the deleted object and remove it from appropriate collections. Will I have to delete the references manually, or is there a automatic way to do it?
Note:
I am using a relational database and from purely SQL-based look I don't see a problem. MembershipOffer table contains FK of both User and Group. When Group is deleted, so are all Offers containing its FK. With their removal the link between Offer and User is deleted. The problem I have is that in the code User has a field Set<MembershipOffer> membershipOffers which may contain a reference to the just-deleted Offer (which will be invalid if I try to access it).
I've got two lists of entities: One that is the current state of the rows in the DB, the other is the changes that were made to the list. How do I audit the rows that were deleted, added, and the changes made to the entities? My audit table is used by all the entities.
Entity listeners and Callback methods look like a perfect fit, until you notice the sentence that says: A callback method must not invoke EntityManager or Query methods! Because of this restriction, I can collect audits, but I can't persist them to the database :(
My solution has been a complex algorithm to discover the audits.
If the entity is in the change list and has no key, it's an add
If the entity is in the db but not the changes list, it's a delete
If the entity is in both list, recursively compare their fields to find differences to audit (if any)
I collect these and insert them into the DB in the same transaction I merge the changes list. But I hate the fact that I'm writing this by hand. It seems like JPA should be able to do this logic for me.
One solution we've come up with is to use an Entity Listener that posts the audits to a JMS queue. The queue then inserts the audits into the database. But I don't like this solution because I think setting up a JMS queue is a pain. It's currently the best solution we've got though.
I'm using eclipselink (ideally, that's not relevant) and have found these two things that look helpful but the JMS queue is a better solution than them:
http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_access_what_changed_in_an_object_or_transaction.3F This looks really difficult to use. You search for the fields by a string. So if I refactor my entity and forget to update this, it'll throw a runtime error.
http://wiki.eclipse.org/EclipseLink/Examples/JPA/History This isn't consistent with the way we currently audit. It expects a special entity_history table.
The EntityListener looks like a good approach since you are able to collect the audit information.
Have you tried persisting the information in a different transaction than the one persisting the changes? perhaps obtaining a reference to a Stateless EJB (assuming you are using EJBs) and using methods marked with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW). In this way the transaction persisting the original changes is put on hold while the transaction of the audit completes. Note that you will not be able to access the updated information in this separate audit transaction, since the original one has not committed yet.
I'm reading a hierarchy of objects with ORMLite. It is shaped like a tree, parents have a #ForeignCollection of 0+ children, and every child refers to its parent with #DatabaseField(foreign=true). I'm reading and saving the whole hierarchy at once.
As I'm new to ORM in general, and to ORMLite as well, I didn't know that when objects with the same ID in the database are read, they won't be created as the actually same object with the same Identity, but as several duplicates having the same ID. Meaning, I'm now facing the problem that (let's say "->" stands for "refers to") A -> B -> C != C -> B -> A.
I was thinking to solve the problem by manually reading them through the provided DAOs and puting them together by their ID, assuring that objects with the same ID have the same identity.
Is there are ORMLite-native way of solving this? If yes, what is it, if not, what are common ways of solving this problem? Is this a general problem of ORM? Does it have a name (I'd like to learn more about it)?
Edit:
My hierarchy is so that one building contains several floors, where each floor knows its building, and each floor contains several zones, where every zone knows its floor.
Is this a general problem of ORM? Does it have a name (I'd like to learn more about it)?
It is a general pattern for ORMs and is called “Identity Map”: within a session, no matter where in your code you got a mapped object from the ORM, there will be only one object representing a specific line in the db (i.e. having it’s primary key).
I love this pattern: you can retrieve something from the db in one part of your code, even do modifications to it, store that object in a instance variable, etc... And in another part of the code, if you get hold of an object for the same “db row” (by whatever means: you got it passed as a argument, you made a bulk query to the db, you created a “new” mapped object with the primary key set to the same and add it to the session), you will end up with the same object. – Even the modifications from before (including unflushed) will be there.
(adding an mapped object to the session may fail because of this, and depending on the ORM and programming language this adding may give you another object back as “the same”)
Unfortunately there is not a ORMLite-native way of solving this problem. More complex ORM systems (such as Hibernate) have caching layers which are there specifically for this reason. ORMLite does not have a cache layer so it doesn't know that it just returned an object with the same id "recently". Here's documentation of Hibernate caching:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html
However, ORMLite is designed to be Lite and cache layers violate that designation IMO. About the only [unfortunate] solution that I see to your issue in ORMLite is to do what you are doing -- rebuilding the object tree based on the ids. If you give more details about your hierarchy we may be able to help more specifically.
So after thinking about your case a bit more #riwi, it occurred to me that if you have a Building that contains a collection of Floors, there is no reason why the Building object on each of the Floors in the collection cannot be set with the parent Building object. Duh. ORMLite has all of the information it needs to make this happen. I implemented this behavior and it was released in version 4.24.
Edit:
As of ORMLite version 4.26 we added an initial take on an object-cache that can support the requested features asked for. Here are the docs:
http://ormlite.com/docs/object-cache
I'm using EJB 3.1 and JPA 2.0.
I'll give you an example to explain my doubts and ask for hints as what is a good practice and what's not.
Let's say I have an EJB that's a user Facade. So you have something like this:
#Stateless
public class UserRepository {
public User find(Long id) {
...do user lookup using entitymanager.
}
}
Alright, now let's say I return the User entity from this and that this entity has a collection of comments (Comment being also an entity).
I could have a Comment Repository with a findCommentsByUser(Long userId) method, or I could fetch the user and call the getComments() method. May be this case is simple, but I've faced this decision many times sometimes not knowing what is best.
Also, let's say I want to add a comment, should I add it to the comment collection the entity has and the have the entity merged, or should I have a addComment(Long userId, Comment newComment) method?
I'm looking for advice or best practices regarding this. If you need further clarifications please do not hesitate to ask.
EDIT:
I've found the comments so far helpful, however notice this, it isn't really about users and comments, I just made that up to explain my situation. It's about whether it is convenient to mix both approaches (which I think is not) or is one better over the other. I liked the "always persist through the repositories" suggestion. But the fact that I have a fetchComments() repository method and the getComments() in the user entity creates to entry points for the same functionality, so how do I deal with that?.
Also the performance (1 query vs 2 queries) isn't really important because I will be fetching the user entity too so it's not like I'm actually saving anything.
We typically only work with detached entities in our applications, so we have a data access manager to fetch and update entities. Then we know that anything we do in our business logic will not be persisted unless specifically called to. I would also fetch the comments with the user entity, but make sure it is not persisted until explicitly called.
I could have a Comment Repository with a findCommentsByUser(Long
userId) method, or I could fetch the user and call the getComments()
I would say that from a performance point of view, the first alternative is slightly better, because you don't fech the user (1 query) and then the comments (another query). The first does it in a single shot.
In the other side, i find the second more readable, abstract, and object oriented approach. I would use this one.
You would generally add the getComments() method to your user object. When you want to add one, you would add it to the user set and then call update on the user object.
I think this highly depends on the requirements, on how fine-grained control of the process do you want to have (this often depends on the performance requirements and expected load, etc.).
If the comments are never to be retrieved independently, I would only keep them as a reference inside User.
If you, however, want to get the comments regardless of the user, or you want to perform some other comments-related queries (like all the comments for users in group A), then I would create separate CommentsRepository.
If you want to be able to add the comment to a user that's not loaded from the DB, but you have the foreign key, you may simply want to add the comment through CommentsRepository like you suggested (also adding a comment to a list of user's comments in parallel and persisting such two lists into DBs may result in 'weird behavior').
There are several considerations that needs to make I hope I will document them here for you.
Domain model is important consideration in EJB3. In your example if you see your domain model allows you to fetch comments lazily because in any flow you show user details first and then his comments.
In some cases your collection (I am referring to comments here) may contain lots of data, In this case its hardly question of string data so not a major concern, but if it would have been real application data then always opt for transient relationship and provide appropriate methods to fetch them independently.
Its never a good practice to expose your collection inside entity bean to outside world so if you OneToMany or ManyToMany relationship then you should provide three basic methods over collections (add, remove, get).
Collections.unmodifiableCollection method should be used while returning collections from get method.
Always use Set when you are using collections inside entities remember they do not allow duplicate.
In your case comments collection has direct dependency on user so you should use cascade type in user to save comments.
I am not convinced why you need UserRepository because em.find method will do job for you.
By default for OneToMany relation fetchtype is lazy so if you want to do eager load you will need to specify it.
I hope these guidelines hopes to solve your problem.
Regards,
Amit
OK, this is a follow-up question to this one, since I am really confused now.
Suppose I have a one-to-many or many-to-many association between entities Person and Event such that a Person class in Java contains a Set<Event>. (Let's ignore whether Event contains a single Person or a Set<Person>.)
Events are entities stored in a database, therefore I can change the event fields. What's the correct way to handle Event mutability and not get the Java Set<> identity checking confused? Are you never supposed to override hashCode() and/or equals() in this case? (e.g. identity = based on object reference identity)
If I want the Events to be ordered (e.g. by an event start time), how do I manage changing the fields of an Event? The database will handle it fine once the change propagates there, but on the Java side, does that mean in order to change an Event in a collection, I have to remove it, change it, and reinsert? Or is there no real way to maintain a sorted order on the Java side of a Hibernate mapping? (and therefore I have to treat it as unordered and therefore handle sorting in Java whenever I want to get a sorted list of Events?)
edit: yuck, I just found these discussions on equals/hashCode:
Hibernate website
a blog
It's not a pitfall at all, it's enforcing the semantics you've told it to expect.
The real problem is "What happens to equality when my so-called key fields change". Yes, it goes right out the window. So, yes you can end up in a situation where someone changes a field in your set, that makes it equal to another, based on those keys.
Yes, your business logic needs to handle that. When you update an Event object, you have to make sure that the new values are valid, not only for the field, but the context you're putting them in. In this case you can't allow the event object being updated to duplicate an existing event.
This problem is even uglier in SQL.