• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Bear Bibeault
  • Liutauras Vilda
  • Devaka Cooray
Sheriffs:
  • Knute Snortum
  • Junilu Lacar
  • Henry Wong
Saloon Keepers:
  • Ron McLeod
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
  • Tim Holloway
Bartenders:
  • salvin francis
  • Frits Walraven
  • Piet Souris

Spring @Transactional, services and DAOs.

 
Bartender
Posts: 1249
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Writing DAOs, which are responsible for  persistence and querying of entities of a given type  is widely considered a good practice.
It's easy to understand that, if we put all the queries and the update / save mechanism in a single object, we obey to the general DRY principle and we can easily add features, like auditing, for instance.
At the same time, another good practice is to write all the business logic in @Service classes: with this approach, we would have services using DAOs for reading and writing entities on database.
Now comes @Transactional, and some design troubles, I'd like you to help me to solve.
Another good practice is to let service handle transactions. With Spring is pretty easy, just annotate with @Transactional a business method.

Now, let's consider following code:



Above example is really easy: via a DAO, the service loads an User entity using userID as key, verifies if the supplied password matches the one saved on the DBMS, and, in that case, sets the current password to the value passed with newPassord parameter.
What's wrong ?
First, the code above is misleading. It seems that the entity, with the new passord, is persisted only if the new password is not empty, but that's not true, because calling or not dao's save method is irrelevant.
At the end of the method, if no exception has been raised, the transaction will be committed.
The second issue i see is that calling the DAO, as previously noted, is not relevant. Maybe the save() method saves some logs; but If I forget to call the DAO save method, I would have my User instance saved nevertheless.

While the  first issue is obviously due to a bad practice - I assign a value to an entity field and only after I perform checks about its validity,  what about the second issue ?
What is - if any - the best practice to "force" a developer to use DAOs methods ? I don't think one could simply set @Transactional on DAOs: what if above method would changed two different entities with two DAOs calls ? It would end up , for example, with only one entity updated if an error
occurs in one of the DAO's service.
Any suggestions ?


 
Ranch Hand
Posts: 1815
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, Claude.
How about only making the save() method transactional, but leaving the getUser() non-transactional?
The changePwd does not need to be transactional.

Scenario 1: if you cannot find the user, then don't do anything.
Scenario 2: if the user is found and the password is validated, then save the user's credentials in a transaction.

As I learn before, the best practice is to keep a transaction to minimum. If some steps don't need to be transactional, then don't include those steps in a transaction.
 
Claude Moore
Bartender
Posts: 1249
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, in this specific case it may be a good idea to make changeUser() not fully transactional, and I fully agree with you when you say that a transaction should be as short as possible.
Anyway, mine is a more conceptual question. If using @Transactional on a @Service or on a @Repository is a long-debated question, and as far as I know most of the consensus is about using @Service together @Transactional.
But, when you have a @Transactional method in a services, at least with respect to saving operations, @Repositories seems to become unnecessary: following your advice, a business transaction should:
- read the entities it needs outside a transaction;
- modify the enties inside a transaction
I don't think it's a clean solution....


 
Claude Moore
Bartender
Posts: 1249
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Himai Minh wrote:Hi, Claude.
How about only making the save() method transactional, but leaving the getUser() non-transactional?



I see what you mean, but you should annotate with @Transactional(NONE) reading methods in DAO...
 
Himai Minh
Ranch Hand
Posts: 1815
12
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, I think it should be @Transaction(Required) for DAO.
But I believe in CrudRepository, the save() is  transactional and you don't need to annotate @Transactional for this method.

Reference:
https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/
 
Claude Moore
Bartender
Posts: 1249
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Himai Minh wrote:Hi, I think it should be @Transaction(Required) for DAO.



The "problem" is that when you use @Transactional on a business logic method, any changes made to entities loaded via repository are performed, when no exceptions are thrown, automatically.
I think that there's no way to enforce calling a repository save method, when used in @Service @Transactional method.
Thanks anyway for your help !!
 
Saloon Keeper
Posts: 21710
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Transactional isn't an either/or attribute. There are several different Transactional scopes. I think that the default is to inherit, but I don't remember. In inherited scope, if a transactional-inherited Service-tier method invokes one or more DAO-tier methods, they run under the same transaction as their caller, and the database changes, unless explicitly flushed, will not be realized until control returns to the service-tier method, and it returns and commits the transaction. At which time, in my preferred architecture, any returned database entities are detached. I only attach from the service tier on down.

Keeping developers out of direct DAO access is a management problem. I have a very strict policy never to invoke a DAO from outside the service tier, even if it means writing a fa├žade service method that exactly mimics the DAO doing the actual work. So if I catch a DAO object being addressed in any other system layer, someone's getting a stern talking to. You don't gain enough efficiency calling a DAO method directly to justify the mental overhead of keeping track of all the violations.
 
Claude Moore
Bartender
Posts: 1249
39
IBM DB2 Netbeans IDE Spring Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Tim, thanks for your answer. I agree fully with your architecture, the only problem is that even with your solution the actual commit happens when the service method returns, and not when "save" methods are called.
We could say that modyfing any property of an entity would be equivalent to a DBMS update / insert of a record. We could also say that, if it's a good practice calling DAOs method within service layer, it's also not necessary for DAOs to expose save methods: actual commit or roll back is all upon the service's shoulders. What I'm missing?
By the way: another approach could be to mark DAOs query methods with @Transactional (NOT_Supported). This way, entities they return are detached and an explicit save on DAOs must be called to persist data.
What do you think about?
 
Tim Holloway
Saloon Keeper
Posts: 21710
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
My persistence Service layers are a combination of business database logic and relational object handling. As I've said elsewhere, anything higher up than this layer deals only with detached (POJO) objects, and is therefore not transactional in database terms. Business database logic is logic required to maintain persistent store integrity and abstraction and should not be confused with non-database business logic (such as, say, computing mortgage amortization tables).

The business methods deal with persistent objects in relation to each other (a "working set"). So you might retrieve a network where you have a customer, recent purchase orders, purchase order details, customer contact info, as a unit. This set might be modified in whole or in part and persisted back as a single transaction.

The DAOs each deal with a single database table (entity type), or, occasionally a parent/child table set if they are bound closely enough. So there'd be a separate DAO for customer, one for customer contact info, one for purchase orders and maybe one for purchase line items.

As far as I'm concerned, NONE of the update should be considered as "real" until the service method returns. So the fact that the data has not been physically transferred to the database before that point is immaterial. All that's required is that I keep a consistent image of the particular working set instance within the current transaction. Whether that image is entirely within ORM objects or helped by meta-data is of no special consequence.

It works well for me. I've done it this way on multiple projects and it's much easier to understand and maintain than what I'd been doing before.
 
mooooooo ..... tiny ad ....
Java file APIs (DOC, XLS, PDF, and many more)
https://products.aspose.com/total/java
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!