SCJP 1.4, SCWCD 1.4 - Hints for you, Certified Scrum Master
Did a rm -R / to find out that I lost my entire Linux installation!
Often the most important part of the news is what they didn't tell.
Tim Holloway wrote:Putting business logic in JPA Entities is Very Bad.
Putting business logic in JPA DAOs is also Bad.
I actually run 2 logic tiers in my JPA projects. The DAO tier does strictly CRUD/find operations. Usually against a single table, but occasionally on a table with children, if it makes sense to do so.
The higher level (service) tier handles my inter-table relationships. It has the persistence-related business logic (such as validating users and the groups that they are in). It's also the boundary between the general business logic and the persistence logic. The persistence services are transactionally bound. Anything above them is detached data and non-transactional. Anything within/below them is transactional and attached (if needed). The DAOs do the grunt work for the services, and they inherit the transactional context of the services that invoke them.
This makes for a nice tidy arrangement where I know exactly where to look for a given functionality, can easily modify and debug it, and makes the various layers plug-replaceable for better code re-use capabilities. Entities in particular can start being used in one app and subsequently find utility in many other apps, which is why app-specific business logic in the entities is especially bad.
Often the most important part of the news is what they didn't tell.
Often the most important part of the news is what they didn't tell.
Rj Ewing wrote:do you know of any projects you would recommend looking at to get a better idea of project structure and how the different layers work together?
Often the most important part of the news is what they didn't tell.
It's written in JSF and since in JSF my policy is that the JSF code and its business methods only work with detached objects, it's the job of the Persistence Service layer to create, retrieve, and update working sets. For example, the trip editor allows me to select bus stops, put them in sequence, and associate a time with the arrival at and departure from each stop on the trip. So the working set is a single trip entity and its stops. The service function to fetch all of this is responsible for invoking the Itinerary DAO to lookup the trip (Itinerary) object, and lazy-fetch the stop objects for it before the whole thing is detached and presented as a POJO tree to the JPA business logic.
This gives an idea about a possible project structure. I am relying heavily on the Spring Framework and for the DAO tier I used Spring Data JPA. Those repositories could easily be replaced with DAOs as well. The database will be properly defined, meaning all required primary keys, foreign keys and possible constraints will be created.
Often the most important part of the news is what they didn't tell.
Often the most important part of the news is what they didn't tell.
Rj Ewing wrote:I am getting the exception when calling User.getMemberProjects().
I think this is because I am first fetching the User when the request begins. Thus it is a seperate Transaction then when I am saving ProjectChild object. Do I need to manually merge the User object? I'm using spring data jpa. Is there a way to automatically merge in this situation?
Roel De Nijs wrote:
The LazyInitializationException is probably the most thrown exception when you are working with an ORM (and/or JPA). Probably everyone working with these technologies has encountered this exception.
And the cause is (probably) very simple: when you nvoke the validate() method, the collection with memberProjects of the User object is not yet initialized, because it's configered to be lazy loaded. When you invoke the method getMemberProjects(), the ORM wants to retrieve the memberProjects from the database but it requires a transaction. Because there is no transaction, the LazyInitializationException is thrown.
I wonder how the create() method is invoked and how the pc object is created. Because I would expect to see a different create() method signature, e.g. create(long projectId, long userId). I am also wondering about the purpose of having the ProjectChild class, because that class didn't exist in your first post.
Often the most important part of the news is what they didn't tell.
Tim Holloway wrote:Actually literally what happens is that when you fetch an object with lazy-load relations, instead of the actual object reference, what you get back in that property is a placeholder that throws the exception when accessed.
That's part of why my Service methods work with working sets of objects not just single objects. The Service method is still within Transactional context (connected), so I can force-load the related objects of interest in the event that their normal mode is lazy fetch.
I also have been known to widen a working set from a business method if it determines that rarely-used or voluminous data that wasn't previously fetched, if required. I'll either refetch using the narrow set's primary object ID, or pass the narrower set to the widening Service method and let it force-load, then return the widened object. Note that such methods may potentially involve returning a different base POJO than the one that they were passed. For example, a JPA merge operation often does that.
If I have a LOT of related objects - for example, if I want to add an employee to a very large department, my Service method may include as its parameters the Employee working set and a basic Department entity object (lazy-fetch on Employees, so no actual unrelated Employees loaded). That allows me to try and save on resources by doing things I can only do within a Transactional, connected context. Such methods WOULD invalidate both the original Employee entity and Department entity, so if I wanted to do further work at the business object level, I'd either have to return the new instances or re-fetch them as any attempt to do further persisting of the original detached objects would throw an Exception. To be precise, I think it would be ConcurrentModificationException.
Often the most important part of the news is what they didn't tell.
Tim Holloway wrote:
If I want to do something like add an employee to a department, I probably wouldn't widen my working set. Instead, I'd usually edit the data and then send it back wholesale for the persistence service to handle. In fact, I might not even fetch the department record at all. It might be sufficient simply to pass department ID into a persistence service like AddMoveEmployeeToDepartment(employee, departmentID). That gives me an opportunity to more behind-the-scenes data optimization. If the department record contains columns such as "numberOfEmployees" that can be computed by the service method, they, too would be covered that way. Only if I actually needed application-specific things done would I fetch the Department record back to the business layer.
Widening is something that I usually only do if it will facilitate the construction of a UI display model. Otherwise, I typically work with the various entities individually.
One useful trick for working within a JPA transaction is that JPA supports the retrieval of entity references. Basically, a reference retrieval returns a handle to the entity without actually fetching the entity from the database. You can use the handle just like an actual entity instance, and it will behave like the entity, but without the local overhead. You can then use this reference to obtain the actual entity without doing an explicity find operation.
Often the most important part of the news is what they didn't tell.
Often the most important part of the news is what they didn't tell.
Tim Holloway wrote:Putting business logic in JPA Entities is Very Bad.
Putting business logic in JPA DAOs is also Bad.
Often the most important part of the news is what they didn't tell.
The only thing that kept the leeches off of me was this tiny ad:
New web page for Paul's Rocket Mass Heaters movies
https://coderanch.com/t/785239/web-page-Paul-Rocket-Mass
|