Hi,
I've asked myself the same questions a while ago in a recent project. Basically, there are 2 ways to go:
1. Use DTOs (Data Transfer Objects), i.e. if your entity should only be updated using a subset of its fields, create some MyEntityDTO class (or several DTO classes for a single entity, each used in a different situation), containing only the needed fields, and ensure your interfaces receive arguments of type MyEntityDTO instead of MyEntity.
This is a very common approach, and is usually chosen for several reasons:
a. large entities waste a lot of network bandwidth, so why pass unnecessary fields back for forth?
b. some roles are not allowed to see all fields of a certain entity, so you have the option of letting different roles run different methods, each passing/receiving a different DTO class.
c. The DTO class is known at runtime, giving developers who write clients a compile-time type checking of what they pass and what they are getting to/from your EJBs.
The downside of this, is that as your entities grow more and more complex, i.e. have derived classes, contain other entities which also have various implementations, etc, then your business interface becomes harder to maintain. In the worst case, if each method has its own DTO, you end up with (methods X entities) DTO class imlementations.
Another problem is complex entities: how do you define a DTO for an entity which contains other entities? Do you also define DTO's for them? What if those entities have derived classes? How do you "map" DTOs to entities?
Bottom line: If you choose this approach, you should try to estimate the level of complexity and expect a lot of DTO-entity mapping code when passing entity data to/from your business interface. You may even consider writing such a mapper module.
2. Do not use DTOs - pass entities as-is, only specify which properties you wish to update. For example, a business method may look like this:
Code:
setEntity(Entity e, Set<String> propertyNames) {
Entity original = em.find(Entity.class, e.getId());
helper.merge(original, e, propertyNames); // this copies only the relevant properties from 'e' to 'original'.
em.merge(original);
}
Same idea may apply for
getEntity(Long id, Set<String> propertyNames) and so on.
For my recent project, I chose this approach, because I estimated that the overall effort is smaller, even for complex entities. I was able to extend the mentioned 'helper' to satisfy most of my needs, so a single 'helper.merge' method covered 99% of my entities. It's also good for security, as I won't pass unneeded info over the wire and into the client. It's also a good compromise for network bandwidth: unneeded fields pass as null, which only lightly impacts bandwidth. However, there is a downside as usual:
a. Client developers don't have compile-time type checking and can only rely on documentation and runtime (i.e. exceptions, debugger) to know which fields are "valid" to pass to/from a certain business method. You should keep documentation and exception handling in sync, to avoid mis-written clients.
b. I had a hard time getting this to work with other EntityManager impls, such as TopLink. Hibernate worked fine with some effort. Specifically, watch out when you return an entity from your EJB to the client, which has lazy props...
good luck