Design of Comment-Feature for multiple database objects - java

In my application I have multiple objects that I would like to add comments to. Every one of these objects is represented in its own database table. Beside being connected to a specific object, all comments share a common context in which the corresponding objects exist. What I tried for now is to use JPA-inheritance with InheritanceType.SingleTable so I can store the foreign keys to every 'commentable'-object in one table and the discriminator-feature to seperate that table into different Comment-Subclasses in JPA:
Superclass Comment.java
#Entity
#Table(name = "COMMENT_TABLE")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "COMMENT_OBJECT_TYPE")
public class Comment {
protected String text;
protected CommonContext context;
...
}
Subclass Object A
#Entity
#DiscriminatorValue(value = "OBJECT A")
public class ObjectAComment extends Comment {
private ObjectA objectA;
// OneToMany-Relation exists in Object A accordingly
#JoinColumn(name = "FK_OBJECT_A")
#ManyToOne
public ObjectA getObjectA() { return objectA; }
public void setObjectA(ObjectA objectA) { this.objectA = objectA; }
}
The other comment-classes for the other objects are designed just as for object A. The common context shall be used to get all comments for a specific situation and I would like to have a comment know its owner, so that I can easily link to that object in my application. Without the latter I had to go through all objects of that type to search for any that has comments, as not every object has them.
On designing the REST-endpoints and the EJBs I ended up creating specific methods for every subclass of Comment.java. For example for creating a comment I have
#POST
#Path("comments/objectA")
public Response createCommentForObjectA(ObjectAComment comment) { ... }
#POST
#Path("comments/objectB")
public Response createCommentForObjectB(ObjectBComment comment) { ... }
...
This feels a bit cumbersome as I would rather have
#POST
#Path("comments")
public Response createComment(Comment comment) { ... }
which is impossible with the current design as I would lose the specific information for the different objects. Now I see three possible ways to go on:
Version 1
I stick with the current solution and create CRUD-methods for every type of comment.
Version 2
A friend suggested, that I could use transient properties in Comment.java:
public class Comment {
...
private COMMENT_OBJECT_TYPE objectType;
private long idObject;
#Transient
public long getIdObject() { return idObject; }
...
#Transient
public COMMENT_OBJECT_TYPE getObjectType() { return objectType; }
...
}
With this I could generalize the parameter of the REST-endpoint and return specific objects depending on the object type:
#POST
#Path("comments")
public Response createComment(Comment comment) {
// return ObjectAComment, ObjectBComment, ... depending on the object type
}
Version 3
Ditch the whole #Inheritance and #DiscriminatorColumn, put everything in one JPA-class and do the whole organizing of the comment context myself. Additionaly I would lose type safety.
None of these solutions feels completely right to me, hence I would like to ask if there is a preferable way to design this kind of comment feature and is there maybe something I am missing completely?
Edit 1
Added information that all comments and objects share a common context. Renamed previous COMMENT_CONTEXT to COMMENT_OBJECT_TYPE to avoid a misunderstanding between this common context and the object type a comment is related to.

It seems to me like your Comment is a standalone entity without dependencies on other entities. A comment has an owner, but should not know who is owning it. So I would add a column on the comment table "ownerUuid". The entire comments "bounded context" knows nothing about the other entities.
When creating a comment you always provide the ownerUuid. Same for retrieval. So you can create comments for any entity having a uuid.
However this means you need to add UUID columns in your already existing tables.
This to me seems like the cleanest solution. This way your comment system can stand on it's own without heavy impact on other entities.
EDIT
Because of the extra information. I would suggest following approach.
Keep in mind that I do not know how the owner of the Comment is used, so the suggestion might not be perfect for this scenario.
As the comment should know the owner object I would suggest doing the following:
Have an interface CommentOwner with methods:
getUuid()
getContext()
Any other information you might need from the owner
A Comment will have a CommentOwner property.
Every entity that you want to contain Comments, should implement this interface. When creating a Comment you provide the CommentOwner.
This way you can retrieve comments Based on Context. A Comments has a direct link to its owner, but still does not need to know about the specific classes of the owners.

In the end I went with version 3, keeping every information in one Comment-class. The subclasses which I would've achieved with #Inheritance and #DiscriminatorColumn would only have one property, the foreign key to the commentable object, and wouldn't differ in what they represent in general and how they would be used.
My class looks something like this now:
public class Comment {
private String text;
private CommonContext context;
private COMMENT_OBJECT_TYPE objectType;
private ObjectA objectA;
private ObjectB objectB;
...
#JoinColumn(name = FK_OBJECT_A)
#ManyToOne
public ObjectA getObjectA() { return objectA; }
public void setObjectA(ObjectA objectA) { this.objectA = objectA; }
...
}

Related

How to ignore subclasses in hibernate entities when persisting

I have a simple entity class like this
#Entity
public class Car{
#Column protected String name;
}
Everything is ok but now I want to use subclasses or anonymous classes of this entity.
public class TestCar extends Car{
TestCar(){ this.name = "Test"; }
}
or
void foo(){
Car c = new Car(){
{ this.name = "Lone"; }
};
// ...
}
These subclasses are just for instantiation purposes and can be ignored completely by hibernate.
But when I try to save the entity I get an "Unknown entity"-Exception.
Is there a way that hibernate ignores such subclasses?
Edit: It would be nice if we could find an answer to the question. It is not helpful (at least not for me) to discuss all the other (well known) pattern. You should see that this is only a strongly simplified example to clarify the problem. (Probably you should just ignore the use-cases)
Just found this possible duplicate (also no solution): How to persist an entity from an non-entity subclass in Hibernate
It is not going to work.
IMHO you should rethink your design. Is realy TestCar or anonymous Car class a subtype of Car? I think you are using wrong tool for creating instances. You can use Builder pattern.

Wicket - Serialization of persisted and non-persisted JPA entities

I know that when using Wicket with JPA frameworks it is not advisable to serialize entities that have already been persisted to the database (because of problems with lazy fields and to save space). In such cases we are supposed to use LoadableDetachableModel. But what about the following use-case?
Suppose we want to create a new entity (say, a Contract) which will consist, among other things, of persisted entities (say, a Client which is selected from a list of clients stored in the DB). The entity under creation is a model object of some Wicket component (say, a Wizard). In the end (when we finish our wizard) we save the new entity to the DB. So my question is: what is the best generic solution to the serialization problem of such model objects? We can't use LDM because the entity is not in the DB yet but we don't want our inner entities (like Client) to be serialized wholly, too.
My idea was to implement a custom wicket serializer that checks if the object is an entity and if it is persisted. If so, store only its id, otherwise use the default serialization. Similarly, when deserializing use the stored id and get the entity from the DB or deserialize using the default mechanism. Not sure, though, how to do that in a generic way. My next thought was that if we can do it, then we do not need any LDM anymore, we can just store all our entities in simple org.apache.wicket.model.Model models and our serialization logic will take care of them, right?
Here's some code:
#Entity
Client {
String clientName;
#ManyToOne(fetch = FetchType.LAZY)
ClientGroup group;
}
#Entity
Contract {
Date date;
#ManyToOne(fetch = FetchType.LAZY)
Client client;
}
ContractWizard extends Wizard {
ContractWizard(String markupId, IModel<Contract> model) {
super(markupId);
setDefaultModel(model);
}
}
Contract contract = DAO.createEntity(Contract.class);
ContractWizard wizard = new ContractWizard("wizard", ?);
How to pass the contract? If we just say Model.of(contract) the whole contract will be serialized along with inner client (and it can be big), moreover if we access contract.client.group after deserialization we can bump into the problem: https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Serialization.2C_and_Detaching
So I wonder how people go about solving such issues, I'm sure it's a fairly common problem.
I guess there are 2 approaches to your problem:
a.) Only save the stuff the user actually sees in Models. In your example that might be "contractStartDate", "contractEndDate", List of clientIds. That's the main approach if you don't want your DatabaseObjects in your view.
b.) Write your own LoadableDetachableModel and make sure you only serialize transient objects. For example like: (assuming that any negative id is not saved to the database)
public class MyLoadableDetachableModel extends LoadableDetachableModel {
private Object myObject;
private Integer id;
public MyLoadableDetachableModel(Object myObject) {
this.myObject = myObject;
this.id = myObject.getId();
}
#Override
protected Object load() {
if (id < 0) {
return myObject;
}
return myObjectDao.getMyObjectById(id);
}
#Override
protected void onDetach() {
super.onDetach();
id = myObject.getId();
if (id >= 0) {
myObject = null;
}
}
}
The downfall of this is that you'll have to make your DatabaseObjects Serializable which is not really ideal and can lead to all kind of problems. You would also need to decouple the references to other entities from the transient object by using a ListModel.
Having worked with both approaches I personally prefer the first. From my expierence the whole injecting dao objects into wicket can lead to disaster. :) I would only use this in view-only projects that aren't too big.
Most projects I know of just accept serializing referenced entities (e.g. your Clients) along with the edited entity (Contract).
Using conversations (keeping a Hibernate/JPA session open over several requests) is a nice alternative for applications with complex entity relations:
The Hibernate session and its entities is kept separate from the page and is never serialized. The component just keeps an identifier to fetch its conversation.

Is it ok to pass interface of DTO to DAO

It's about passing interface of DTO to DAO.
For example I have following code
public interface User {
String getName();
}
public class SimpleUser implements User {
protected String name;
public SimpleUser(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
// Mapped by Hibernate
public class PersistentUser extends SimpleUser {
private Long id;
// Constructor
// Getters for id and name
// Setters for id and name
}
I'm using generic DAO. Is it ok if I create DAO with using interface User instead PersistentUser?
User user = new PersistentUser(name);
UserDao.create(user);
I read a lot of topics on stack but not figured out is this approach ok or no. Please help me. Maybe this is stupid and I can achive only problems.
About separating beans.
I did this because some classes I want to share via API module, that can be used outside to create entities and pass them to my application. Because they uses interface I developed so I can pass them to my DAO for persisting.
Generally, I would say it is ok, but there are a few hidden problems. A developer could cast the object down or access some state via a toString method that shouldn't be accessible. If you don't be careful, it could happen that state is serialized as JSON/XML in webservices that shouldn't be serialized. The list goes on.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(PersistentUser.class)
interface User {
String getName();
}
Querying could look like this
List<User> dtos = entityViewManager.applySetting(
EntityViewSetting.create(User.class),
criteriaBuilderFactory.create(em, PersistentUser.class)
).getResultList();

Persisting third-party classes with no ID's

Say I have the following Java class, which is owned by a vendor so I can't change it:
public class Entry {
private String user;
private String city;
// ...
// About 10 other fields
// ...
// Getters, setters, etc.
}
I would like to persist it to a table, using JPA 2.0 (OpenJPA implementation). I cannot annotate this class (as it is not mine), so I'm using orm.xml to do that.
I'm creating a table containing a column per field, plus another column called ID. Then, I'm creating a sequence for it.
My question is: is it at all possible to tell JPA that the ID that I would like to use for this entity doesn't even exist as a member attribute in the Entry class? How do I go about creating a JPA entity that will allow me to persist instances of this class?
EDIT
I am aware of the strategy of extending the class and adding an ID property it. However, I'm looking for a solution that doesn't involve extending this class, because I need this solution to also be applicable for the case when it's not only one class that I have to persist, but a collection of interlinked classes - none of which has any ID property. In such a scenario, extending doesn't work out.
Eventually, I ended up doing the following:
public class EntryWrapper {
#Id
private long id;
#Embedded
private Entry entry;
}
So, I am indeed wrapping the entity but differently from the way that had been suggested. As the Entry class is vendor-provided, I did all its ORM work in an orm.xml file. When persisting, I persist EntryWrapper.
I don't have much experience with JPA, but I wouldn't extend your base classes, instead I would wrap them:
public class PersistMe<T> {
#Id
private long id;
private T objToWrap;
public(T objToWrap) {
this.objToWrap = objToWrap;
}
}
I can't test it, if it doesn't work let me know so I can delete the answer.

Exclude field in JPA Entity Listener

I have an entity class in my Enterprise Java application that has an entity listener attached to it:
#Entity
#EntityListeners(ChangeListener.class)
public class MyEntity {
#Id
private long id;
private String name;
private Integer result;
private Boolean dirty;
...
}
However, I would like it so that the entity listener got triggered for all fields except the boolean one. Is there any way exclude a field from triggering the entity listener without making it transient?
I'm using Java EE 5 with Hibernate.
However, it is possible if you implement your own solution. I've had the same need for audit log business requirement, so designed my own AuditField annotation, and applied to the fields to be audit-logged.
Here's the example in one entity bean - Site.
#AuditField(exclude={EntityActionType.DELETE})
#Column(name = "site_code", nullable = false)
private String siteCode;
So, the example indicates the 'siteCode' is a field to audit log, except DELETE action. (EntityActionType is an enum and it contains CRUD operations.)
Also, the EntityListenerhas this part of code.
#PostPersist
public void created(Site pEntity) {
log(pEntity, EntityActionType.CREATE);
}
#PreUpdate
public void updated(Site pEntity) {
log(pEntity, EntityActionType.UPDATE);
}
#PreRemove
public void deleted(Site pEntity) {
log(pEntity, EntityActionType.DELETE);
}
Now what it has to do in log() is, to figure what fields are to audit log and what custom actions are involved optionally.
However, there's another to consider.
If you put the annotation at another entity variable, what fields of the entity have to be logged? (i.e. chained logging)
It's your choice whether what are annotated with #AuditField only in the entity or some other ways. For my case, we decided to log only the entity ID, which is a PK of a DB table. However, I wanted to make it flexible assuming the business can change. So, all the entites must implement auditValue() method, which is coming from a base entity class, and the default implementation (that's overridable) is to return its ID.
There is some kind of mixing of concepts here. EntityListeners are not notified about changes in attribute values - not for single attribute, neither for all attributes.
For reason they are called lifecycle callbacks. They are triggered by following lifecycle events of entity:
persist (pre/post)
load (post)
update(pre/post)
remove (pre/post)
For each one of them there is matching annotation. So answer is that it is not possible to limit this functionality by type of persistent attributes.

Categories

Resources