Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

UsingHibernateWithVaadin.asciidoc 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. ---
  2. title: Using Hibernate With Vaadin
  3. order: 17
  4. layout: page
  5. ---
  6. [[using-hibernate-with-vaadin]]
  7. = Using Hibernate with Vaadin
  8. Using Hibernate in Toolkit application, Basic
  9. http://en.wikipedia.org/wiki/Create,_read,_update_and_delete[CRUD]
  10. actions for persistent POJO
  11. image:img/screenshot.png[Example CRUD application]
  12. Check out related source code with subversion (svn co
  13. http://dev.vaadin.com/svn/incubator/hbncontainer/) or view it with trac
  14. http://dev.vaadin.com/browser/incubator/hbncontainer/. Download the
  15. latest version as a Vaadin add-on from the Vaadin Directory (https://vaadin.com/directory/component/hbncontainer)
  16. _The project in incubator currently has a prototype of using
  17. associations. The article is outdated on that part_.
  18. Hibernate is the de facto standard when it comes to Java and Object
  19. Relational Mapping. Since version 3 onwards one can actually drop the de
  20. facto part as Hibernate 3 implements Java Persistency API with some
  21. optional packages. Hibernate is backed by a strong support from both
  22. commercial players and open source community. It is an important part of
  23. popular JBoss Application Server.
  24. As an open source project with an industry proven maturity, Hibernate
  25. makes a perfect combo with IT Mill Toolkit. Hibernate is in a key role
  26. in many projects built or supported by IT Mill. The way Hibernate is
  27. used varies a lot due different kinds of architectures and requirements.
  28. Largest questions are usually how to work with Hibernate session,
  29. transactions and how to tie entity beans into toolkit components.
  30. In this article and example application I'll show you how to implement
  31. session-per-request pattern for Hibernate session handling and present
  32. some patterns to do
  33. http://en.wikipedia.org/wiki/Create,_read,_update_and_delete[CRUD]
  34. actions of a simple entity bean. As I'm a sport fanatic, instead of
  35. storing cats and other mammals to DB we'll build a simple *WorkoutLog*
  36. application to store the details of our jogging sessions. Download the
  37. source package to see full source code.
  38. Note that this is not trying to be a yet another Hibernate tutorial.
  39. Although we'll stay in rather basic tricks, I expect the reader to have
  40. some experience on ORM and IT Mill Toolkit. The purpose of this tutorial
  41. is to show an example how to do simple Hibernate session handling in
  42. Toolkit application and explain some patterns how to entity objects can
  43. be tied into GUI.
  44. [[preparing-the-project]]
  45. Preparing the project
  46. ~~~~~~~~~~~~~~~~~~~~~
  47. If you want want to learn by doing, it is time to put your hands on
  48. dirt. Create a new web application project in your favorite IDE, throw
  49. in latest `toolkit.jar` and all needed Hibernate related libraries.
  50. Prepare your database and configure Hibernate. Combo I chose when
  51. writing this article was Eclipse, WTP and MySQL 5, but any option should
  52. be fine.
  53. If you want to get started really easily, check out the Eclipse project
  54. from svn repository. This is done simply with subclipse plugin or via
  55. command line svn co http://dev.vaadin.com/svn/incubator/hbncontainer/.
  56. The project containtains embedded database( http://hsqldb.org/[HSQLDB]
  57. ), all needed required libraries and the source code for the example
  58. project itself. That is an easy way to start experimenting with Toolkit
  59. and Hibernate. You will also need a servlet container, Tomcat is a good
  60. option.
  61. As I hate all xml configuration I created DB mappings with annotations.
  62. Below is the one and only entity class we'll be using in this example.
  63. Create it in and possibly test your Hibernate configuration with a
  64. simple test application.
  65. [source,java]
  66. ....
  67. @Entity
  68. public class Workout {
  69. @Id
  70. @GeneratedValue(strategy=GenerationType.AUTO)
  71. private Long id;
  72. private Date date = new Date();
  73. private String title = " -- new workout -- ";
  74. private float kilometers;
  75. public Workout() {}
  76. public Long getId() {
  77. return id;
  78. }
  79. private void setId(Long id) {
  80. this.id = id;
  81. }
  82. public Date getDate() {
  83. return date;
  84. }
  85. public void setDate(Date date) {
  86. this.date = date;
  87. }
  88. public String getTitle() {
  89. return title;
  90. }
  91. public void setTitle(String title) {
  92. this.title = title;
  93. }
  94. public float getKilometers() {
  95. return kilometers;
  96. }
  97. public void setKilometers(float kilometers) {
  98. this.kilometers = kilometers;
  99. }
  100. }
  101. ....
  102. Also create a new Tookit application, configure it in web.xml.
  103. [[using-session-per-request-pattern]]
  104. Using session-per-request pattern
  105. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  106. Proper session handling in Hibernate backed applications is often the
  107. most difficult problem. Use cases vary from by architecture and load.
  108. Hibernate is known to be quite strict on session and transaction
  109. handling, so to save yourself from a headache, I'd suggest you to make
  110. it right. There is a lot's of good documentation about different session
  111. handling patterns in hibernate.org.
  112. Using session-per-request pattern is often a safe bet for Toolkit
  113. application. It is maybe the most common pattern among all Servlet based
  114. applications. When doing data manipulation we'll use the same session
  115. during the whole request and in the end of the request make sure that
  116. session and transaction is properly finalized. When implemented
  117. properly, session-per-request pattern guarantees that number of
  118. Hibernate sessions is in control, sessions are properly closed and
  119. sessions are flushed regularly. A good combo of characteristics for a
  120. multi-user web application.
  121. By Toolkits nature, session-per-request pattern is actually kind of
  122. wrong. Toolkit is a general purpose GUI framework and programmer does
  123. not need to think about requests and responses at all. Actually Toolkit
  124. applications and components don't know nothing about requests. It its
  125. the web terminal that does all the web magic. Another option is to use
  126. session-per-application or even session-per-transaction like one would
  127. do with SWING or other destop application. Always evaluate your
  128. requirements, use cases and available computing resources to have the
  129. optimal session handling pattern.
  130. To ensure that we are using only one Hibernate session per http request
  131. is the easy part. We can use Hibernates `getCurrentSession()` to retrieve
  132. thread local session instance. As we always want to actually use the
  133. session I build a helper method that will also begin a database
  134. transaction. In our *WorkoutLog* we will always be using this method to
  135. get session reference.
  136. [source,java]
  137. ....
  138. /**
  139. * Used to get current Hibernate session. Also ensures an open Hibernate
  140. * transaction.
  141. */
  142. public Session getSession() {
  143. Session currentSession = HibernateUtil.getSessionFactory()
  144. .getCurrentSession();
  145. if(!currentSession.getTransaction().isActive()) {
  146. currentSession.beginTransaction();
  147. }
  148. return currentSession;
  149. }
  150. ....
  151. Closing is bit more tricky. One way around would be to use a servlet
  152. filter. You can find examples of this from hibernate.org. But we'll keep
  153. toolkits terminal independence in mind and don't pollute our program
  154. with servlet specific code. To properly implement session-per-request
  155. pattern we'll need to familiarize ourselves to a feature in Toolkits
  156. terminal. Ideally toolkit programmer don't need to care about terminal
  157. at all, but now we need to hook some logic into the end of (http)
  158. request that don't exist for the application. For the pattern it is
  159. essential that session finalization is done always and and after all
  160. hibernate related stuff is done. With event based programming model
  161. there is no way we can detect the last database action in the actual
  162. program code.
  163. The feature we need is `TransactionListeners`. `TransactionListeners` are
  164. attached to `ApplicationContext` which corresponds to http session in our
  165. current web terminal. `TransactionListeners` are notified right before
  166. and right after the clients state is synchronized with server. The
  167. transaction end is what we need here. I'll attach the transaction
  168. listener in the applications `init()` like this:
  169. [source,java]
  170. ....
  171. getContext().addTransactionListener(new TransactionListener() {
  172. public void transactionEnd(Application application,
  173. Object transactionData) {
  174. // Transaction listener gets fired for all contexts
  175. // (HttpSessions) toolkit applications, checking to be this one.
  176. if (application == WorkoutLog.this) {
  177. closeSession();
  178. }
  179. }
  180. public void transactionStart(Application application, Object transactionData) {
  181. }
  182. });
  183. ....
  184. In `closeSession()` the usual Hibernate sessions finalization is done.
  185. [source,java]
  186. ....
  187. private void closeSession() {
  188. Session sess = HibernateUtil.getSessionFactory().getCurrentSession();
  189. if(sess.getTransaction().isActive()) {
  190. sess.getTransaction().commit();
  191. }
  192. sess.flush();
  193. sess.close();
  194. }
  195. ....
  196. The sequence diagram below shows how Session handling works with this
  197. pattern during one (http) request. It is an imaginary server visit that
  198. fires to event listeners. The first one does some listing and the latter
  199. re-attaches detached pojo. Note that the second database/Hibernate
  200. action uses the same Session object as the first one. Note that function
  201. names are not real ones, but trying to describe the process better.
  202. image:img/sd_s_per_r.gif[Session handling sequence diagram]
  203. Due Toolkit applications do have state, pattern can be defined more
  204. strictly as a session-per-request-with-detached-objects pattern. As the
  205. session closes quite often, our entity objects are most likely detached
  206. by the time we are updating them. So when we have our changes to entity
  207. object done, it is time to re-attach it to current session to persist
  208. changes into database. An example of that is below:
  209. [source,java]
  210. ....
  211. run.setDate((Date) date.getValue());
  212. run.setKilometers(Float.parseFloat(kilomiters.getValue().toString()));
  213. run.setTitle((String) title.getValue());
  214. getSession().merge(run);
  215. ....
  216. [[attaching-pojos-ui]]
  217. Attaching POJO's UI
  218. ~~~~~~~~~~~~~~~~~~~
  219. In this chapter I'll discuss briefly some options to implement basic
  220. CRUD (Create, Read, Update, Delete) actions for our DB backed Workout
  221. objects.
  222. [[listing-objects]]
  223. Listing Objects
  224. ^^^^^^^^^^^^^^^
  225. If you are learning by doing, I'd suggest that you manually insert some
  226. rows to your db at this point. Listing an empty database will be quite
  227. boring.
  228. The most natural way to list our simple Workout object is to put them
  229. into Table component. To do this there is an easy way and an the right
  230. way. We'll start with the easy one, but I suggest to use the latter in
  231. real applications. The code below (the "easy" way) is not in the
  232. *WorkoutLog* app at all, but you can try it if you want.
  233. [source,java]
  234. ....
  235. // prepare tables container
  236. table.addContainerProperty("date", Date.class, null);
  237. table.addContainerProperty("kilometers", Float.class, null);
  238. table.addContainerProperty("title", String.class, null);
  239. // list all Workouts
  240. List workouts = getSession().createCriteria(Workout.class).list();
  241. for (Iterator iterator = workouts.iterator(); iterator.hasNext();) {
  242. Workout wo = (Workout) iterator.next();
  243. // add item to table and set properties from POJO
  244. Item woItem = table.addItem(wo.getId());
  245. woItem.getItemProperty("date").setValue(wo.getDate());
  246. woItem.getItemProperty("kilometers").setValue(wo.getKilometers());
  247. woItem.getItemProperty("title").setValue(wo.getTitle());
  248. }
  249. ....
  250. In the above example we are using Table's default container,
  251. `IndexedContainer`. It is a good general purpose container, but using it
  252. always is not a good option. You have to load the data into it by
  253. yourself and configure properties etc. It also stores everything in
  254. memory. In our example it may start to be a problem if you
  255. do three workouts everyday, live 100 years old and memory chips don't
  256. get cheaper in the future. But in real application we might really have
  257. millions of records in DB. I really wouldn't suggest to load that table
  258. into memory anymore.
  259. As you may guess the way is to build our own container for Workouts.
  260. Building good containers is one of the most difficult tasks in Toolkit
  261. programming. There are number of different sub interfaces one might want
  262. to implement and a whole bunch of methods code. Luckily one can't safely
  263. throw `UnsupportedOperationExeception` for many of those. It is a boring
  264. tasks, but it often pays it back later. When you have your container
  265. ready, it hides lots of DB access from program logic and can be used for
  266. many components (Selects, Trees, Tables etc). With your own customized
  267. container you can also tune it to work as you want (memory-consumption
  268. versus speed etc).
  269. As building a full-featured is not in the scope of this article, it is
  270. time to throw in a nice helper class called `HbnContainer`. It takes a
  271. Hibernate entity class and a strategy to get Hibernate session in its
  272. constructor. It is indexed, ordered, sortable, had a limited supports
  273. adding/removing items and even ought to be fairly well scalable (by
  274. number of rows in DB). It is not part of Toolkit as we don't consider it
  275. ready for framework yet, but we hope to have something similar in the
  276. core Toolkit in later releases. But feel free to use it in you own
  277. projects.
  278. With `HbnContainer` loading table with Workouts simplifies quite a bit.
  279. We need to implement `HbnContainer`.`SessionManager` interface, but it is
  280. rather easy task as we already have getSession named function in our
  281. *WorkoutLog*. Create and add table to your application, load its content
  282. with following code snippet and you should have a Workout listing on
  283. your screen.
  284. [source,java]
  285. ....
  286. table.setContainerDataSource(new HbnContainer(Workout.class, this));
  287. ....
  288. [[creating-workouts]]
  289. Creating workouts
  290. ^^^^^^^^^^^^^^^^^
  291. Now that we have listing we might want to add some rows via our web
  292. interface. To create a new Workout instance and store it in to DB we
  293. have to do the usual Hibernate stuff: instantiate POJO and attach it to
  294. session. But as I hinted earlier, having a good container will help us
  295. to do it even simpler. `HbnContainer` supports adding items with the most
  296. simplest method `addItem()`.
  297. If you look into the implementation, it does all the usual Hibernates
  298. stuff and returns items generated identifier. In addition this it also
  299. notifies appropriate listeners that the content of table has changed. So
  300. by using containers `addItem()` method instead of doing DB persist
  301. ourselves we don't need to worry about UI updates. Table listens to its
  302. container changes and changes gets sent to web browsers.
  303. [[updates-and-deletes]]
  304. Updates and deletes
  305. ^^^^^^^^^^^^^^^^^^^
  306. Building an editor for our Workout object is a straight forwarded coding
  307. task. You may organize your code just like you want. `WorkoutEditor`
  308. class is a simple example implementation that shows and editor in
  309. floating window. It has fields for workouts properties and it can be
  310. loaded with Workout instance or with an identifier. In `WorkoutLog` I
  311. attached a `ValueChangeListener` into table to open editor when user
  312. clicks a row in table. Save and delete buttons in `WorkoutEditor`
  313. delegates work back to methods in main application. Delete uses
  314. containers method and behind the scenes a normal Hibernate object
  315. deletion. When saving we just reattach detached object using `merge()`.
  316. To avoid "monkey-coding" I'll show one can to use toolkits advanced
  317. features to automatically create editable fields for items. The
  318. `WorkoutEditor` class could have created its fields automatically by
  319. using appropriate Item and a Form component. Also Table supports
  320. automatic field generation, so why not edit workouts directly in our
  321. main object listing?
  322. All we need to do is to use `setEditable()` method. In `WorkoutLog` there
  323. is a button that toggles this feature. Clicking it make table editable,
  324. clicking it again shows data only. Can't imagine any simpler way to do
  325. the 'U' part of CRUD.
  326. Both Form and Table components use `FieldFactory` interface to
  327. automatically create fields for Items properties. There is a simple
  328. default factory that you almost certainly want to modify for your needs.
  329. As an example I extended it to set proper resolution for date field and
  330. also did some other fine tuning.
  331. If you investigate the code a bit you might wonder how the database is
  332. updated now as we don't seem to call `merge()` or any other method to
  333. re-attached POJO. When field is updated it knows only about its
  334. underlaying Property. In this case it is `EntityItemProperty` built by
  335. `HbnContainer`. Field calls its `setValue()` method and that is where the
  336. underlaying POJO is re-attached into Hibernate session.
  337. [[adding-custom-columns-to-hbncontainer]]
  338. Adding custom columns to HbnContainer
  339. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  340. This last bonus chapter is bit out of scope of the article. But as
  341. updating is so easy in Table we could ditch our `WorkoutEditor`. But then
  342. arises a question how to implement deletion. An option is to use Tables
  343. selection feature and "Delete selected" button. Another one is to use
  344. context menu option. This is also done in `WorkoutLog`. Both are good
  345. options, but someday someone will be asking how to add delete button on
  346. each row. So lets discuss that right away.
  347. Ideologically this is adding a new property to our items. We definitely
  348. don't want to pollute our entity object by adding `public Button
  349. getDelete()` to our Workout object. The right place to implement this is
  350. in custom Container and Item. I implemented an example of this by
  351. extending `HbnContainer` to `WorkoutListingWithSteroids`. It adds a column
  352. "actions" (or container property if we are talking "Toolkit") which is a
  353. layout containing two buttons.
  354. Another possibly little bit easier method is to use recently introduced
  355. feature in Table component called `ColumnGenerator`. *WorkoutLog* (in svn)
  356. has an example of this method too.
  357. Check out the example code if you want this kind of behavior.
  358. [[summary]]
  359. Summary
  360. ~~~~~~~
  361. Popular open source ORM tool Hibernate is a perfect companion for IT
  362. Mill Toolkit. Finding the right way to handle session in your
  363. application is a often the most critical task. Session-per-request
  364. pattern is a safe choice for Toolkit application, but not the only
  365. option. DB backed entity objects are used in a usual manner. To use more
  366. advanced features of toolkit, you'll want to use a custom built
  367. container-item-property set. ORM is never easy, but it is not a rocket
  368. science if you use tested industry proven patterns. And if your
  369. application is going to be a big or old, I can guarantee that you will
  370. have a nice ROI for hours you spend on it (ORM).