I'll clarify a little. In general, you want to keep persistence code, ui code, and business rules fairly separate. This will keep your application much more flexible as more and more requirements are added over time.
In your example code, you've got persistence embedded directly in ui code. Should you ever decide to replace your perisistence layer with something else (say, replace toplink with hibernate, ibatis, or even raw jdbc), you will have to go in and modify every page class you've built that has persistence code in it. In a large project, this is effectively impossible to do without destabilizing the codebase too much to be acceptable. Therefore, most well architected apps will keep all perisistence code in a separate set of interfaces. So long as you can create a persistence layer that maintains the same interface, you can replace one with another very simply. This is where dependency injection becomes useful - Instead of injecting a toplink data access object into your page, you can inject a hibernate one instead. But wait, did I just say injecting a dao into the page? Most of us would argue you never want to do that, for the following reason. Suppose you have a method in your DAO which stores an entity into the database. If you are using your dao directly in your page class, then you either have to open and commit a transaction directly in your page (violating the rule about keeping persistence code out of your ui layer and once again locking you into a particular transaction mechanism, preventing you from switching between JDBC transactions, JTA transactions, etc), or else your DAO itself has to open the transaction, save the entity, and commit the transaction. This would be fine in this circumstance. Your persistence code would stay in your persistence layer, but now you have another problem. Page1 may store just a single instance of the entity, so including transaction semantics around that store operation works just fine. But now you have to add Page2, and Page2 wants to store two entities. But if either store() fails (unique column conflict, for instance), you don't want to store() either of the entities. So now your transaction within the store method won't work, since the first store() will be fully committed before the second store() fails, offering you no way to roll back. So your only option is to implement another DAO method - one which takes a collection of entities and stores them all within a single transaction. You can probably see that as your project grows, you are going to wind up with a lot of very similar methods, each different only in the transaction behaviour. The rule that you must be able to store both entities if you are going to store either one is considered a 'business rule' - an artificial constraint on what you are allowed to do. Business rules frequently change over the life of a project, so once again, you want to keep your business logic firmly separated from your ui. Again, this is accomplished by shoving your business logic into separate classes with a well defined interface and injecting instances of those interfaces into your page classes. Now, your ui code doesn't need to know anything about transactions because your service layer is handling them. Your ui can call a method to store() two entities and doesn't need to be concerned with the details of how that occurs or what is allowed. All it knows is it either succeeded or failed based on an exception may catch. Once again, your ui layer is clean. It is injected only with service interfaces. Service objects are injected with DAO interfaces, and you've got a nice clean layer separation. Except for one thing. You've got a bunch of transaction code sitting in your service layer, wrapped around calls into your DAOs, which no longer have any knowledge of transactions themselves. This means there is still a strong binding between your service layer and your perisistence layer, making it very difficult to modify one without modifying the other. That's generally considered a _bad_thing_ and should be avoided. So this is where Spring steps in and provides some VERY useful functionality. Spring gives a nice declarative mechanism (ie. you can set it up in a config file or annotation, rather than by writing code) for forcing a transaction around any method of any object. You can define a business method that calls multiple DAO methods and you can tell spring that anytime someone calls that method, you want a transaction to be started before the method executes, and the transaction to be committed when the method is completed without throwing an exception, or rolled back if an exception is caught. Now you can remove all of that pesky transaction code from your service layer, giving you true independance from your persistence layer outside of the persistence interfaces, which should be generic across any persistence mechanism. OK, you've still got a dependency in your config, but at least your code, which is the most complex and error prone piece of the application, is completely independant (that's why I prefer to avoid annotations for transaction declarations. I don't want to recompile to change a transaction) A side benefit of all of this is that Spring is now responsible for all that boilerplate code that gets copied around everywhere for opening a connection, catching exceptions, closing the connection, catching more exceptions, etc. Spring is widely used and well tested, so you can be confident that the boilerplate code it is using is correct. The one caveat (and it is a small one), is that your persistence layer is required to use certain helper methods provided by spring which will ensure that your dao uses the connection that has the transaction on it. However, the helper methods are incredibly useful and do much of the work involved in communicating with your database for you. I don't know toplink, as I use hibernate, but let me give you a quick hibernate example. In a normal Hibernate DAO, if I wanted to store an object into the db, I'd do the following: public void store(Event theEvent) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); try { session.beginTransaction(); session.save(theEvent); session.getTransaction().commit(); } catch (Exception e) { try { session.getTransaction().rollback(); } catch (Exception e2) { } } finally { try { HibernateUtil.getSessionFactory().close(); catch (Exception e3) { } } } That's a lot of code, especially those obnoxious nested try...catch..finally blocks that are so common to all db programming in java. Even without the transaction handling, I've still got to get the session and execute the store() method. Using the Spring hibernate helper class and configuration however, I can do the same thing in a single line of code: public void store(Event theEvent) { getHibernateTemplate().saveOrUpdate(theEvent); } The HibernateTemplate already knows how to get the current session (which will already have a transaction started on it). If the store method throws an exception, it will be propagated up and the transaction will automatically be rolledback. Spring also translates database and ORM specific exceptions into nice generalized exceptions so that your application exception handling doesn't need to know about the ORM or db. Otherwise, you have to know that error code 6453 in oracle is equivalent to error code 1234 in postgres, or else just respond to a generic SQLException. Spring can throw a much more meaningul exception that will be the same no matter which db you use, allowing your app to respond much more intelligently to exceptions up to and including providing much more meaningful error messages to users and developers. There are methods provided for all the basic CRUD operations (create, retrieve, update, delete) as well as various list methods and, of course, a mechanism for building complex queries with lots of parameters. It saves an awful lot of code. So now, if you use all the advice I've offered so far, you've got the following: a DAO layer which has no transaction semantics and which uses Spring DAO base classes to handle all the complexity of db interaction, whether you use hibernate, iBatis, JDBC, or toplink. (http://static.springframework.org/spring/docs/1.2.x/api/org/springframework/orm/toplink/package-summary.html and http://static.springframework.org/spring/docs/1.2.x/api/org/springframework/orm/toplink/support/package-summary.html ) You have a business service layer which ensures your application adheres to business rules.. You have transactions that are assigned to your service methods declaratively (either through the spring config or via annotations. I prefer the config file because I often like to set things up differently in a test environment compared to production and I don't want to have to recompile) You have a UI layer that knows nothing about any of this, other than the interfaces provided by your business service layer. You are now free to swap your web ui for a swing ui, WAP ui, command line ui, or anything else you care for, without being forced to ensure that all your business rules and persistence code is properly copied to the new ui layer. You can also replace your perisistence layer with hibernate, jdbc, or anything else without touching a single line of code in either your service or web ui layers. You've coded everything to a set of interfaces instead of specific classes. This is important because it means you can easily drop different implementations of the same interface into your code via dependency injection, including injecting mock objects during testing in order to simulate production conditions. The one complaint I have about a rigidly layered application such as this is that it can force you into an unreasonable amount of typing to do simple things. If you add a new DAO method to support some action, you've now got to add the method to the dao interface and the dao implementation. There is also a high probability you'll be adding a corresponding method to your service layer, so you've got to add methods to the service interface and the service implementation as well. Then you've got to make sure you've applied the correct transaction semantics to the service method in question. It is a fair amount of cut and paste typing, which can be error prone, although errors are likely to be caught at compile time (unless you screw up your transaction declaration, which you won't detect until runtime), so it isn't a huge problem, other than the time involved and the repetitive stress injuries. I don't use an IDE, so I have some quick scripts which dump all the necessary method and transaction declarations into a file, and I can then just cut and paste the guaranteed correct declarations into the appropriate files (it was easier than trying to parse the files and inserting the code directly and it means I can relax a little on file/class naming conventions). Also, judicious use of base interfaces and base classes with generics means that you can often add a single method to a base class in order to get the functionality added for every single entity type in your application. OK, that was a ton of information, but hopefully you'll find it very useful, since you said you were new to Java web development. --sam On 12/7/06, Cyrille37 <[EMAIL PROTECTED]> wrote:
Hello D&J Gredler a écrit : > I haven't used EntityManagers directly, but your code looks fine to me, > especially if it works ;-) > You may want to wrap your flush( ) call in a try/catch block in case > something goes wrong, and roll back the transaction in the catch block. It works, but I've made change because I've got only one EntityManager for all the application. In Toplink's examples I see that the create an EntityManager each time they make a query. So I've change flush() to merge(this_product) because object was "detached" ... Sorry, I'm not very clear ... > Once you get more comfortable with things (and if you're using > Hibernate), > you may want to check out some of the integration packages (Tapernate, > Honeycomb, etc) that handle transaction scoping for you. hmmm... Which scope ? I could not imagine what they do. I'm to beginner with Java & Co. Please, can you explain me a beat what a package like Tapernate or Honeycomb will do. For da moment I'm using JPA (TopLink Essential). > Spring might also > be a good option if you end up splitting your app up into presentation / > service / persistence layers. At the beginning of my exploration, I've try Spring with Tapestry and I did not see what it will bring to me more than Hivemind. Perhaps because I only the the object injection, and not other things that Spring bring to us. Thanks for you support ;o) Cyrille --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]