View Javadoc
1   /*
2    * JBoss, Home of Professional Open Source
3    * Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
4    * contributors by the @authors tag. See the copyright.txt in the
5    * distribution for a full listing of individual contributors.
6    *
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.jboss.as.quickstarts.bmt;
18  
19  import javax.inject.Inject;
20  import javax.persistence.EntityManager;
21  import javax.persistence.EntityManagerFactory;
22  import javax.persistence.PersistenceUnit;
23  import javax.transaction.RollbackException;
24  import javax.transaction.Status;
25  import javax.transaction.UserTransaction;
26  
27  import java.util.List;
28  
29  /**
30   * A class for updating a database table within a JTA transaction. Since the class is only a simple CDI bean the developer is
31   * responsible for both controlling the life cycle of the Entity Manager and for transaction demarcation.
32   *
33   * @author Mike Musgrove
34   */
35  public class UnManagedComponent {
36      /*
37       * Inject an entity manager factory. The reason we do not inject an entity manager (as we do in ManagedComponent) is that
38       * the factory is thread safe whereas the entity manager is not.
39       * 
40       * Specify a persistence unit name (perhaps the application may want to interact with multiple databases).
41       */
42      @PersistenceUnit(unitName = "primary")
43      private EntityManagerFactory entityManagerFactory;
44  
45      /*
46       * Inject a UserTransaction for manual transaction demarcation (this object is thread safe)
47       */
48      @Inject
49      private UserTransaction userTransaction;
50  
51      public String updateKeyValueDatabase(String key, String value) {
52          EntityManager entityManager = entityManagerFactory.createEntityManager();
53  
54          try {
55              userTransaction.begin();
56  
57              /*
58               * Since the Entity Manager (EM) is not managed by the container the developer must explicitly tell the EM to join
59               * the transaction. Compare this with ManagedComponent where the container automatically enlists the EM with the
60               * transaction. The persistence context managed by the EM will then be scoped to the JTA transaction which means
61               * that all entities will be detached when the transaction commits.
62               */
63              entityManager.joinTransaction();
64  
65              // make some transactional changes
66              String result = updateKeyValueDatabase(entityManager, key, value);
67  
68              /*
69               * Note that the default scope of entities managed by the EM is transaction. Thus once the transaction commits the
70               * entity will be detached from the EM. See also the comment in the finally block below.
71               */
72              userTransaction.commit();
73  
74              return result;
75          } catch (RollbackException e) {
76              // We tried to commit the transaction but it has already been rolled back (adding duplicate keys would
77              // generate this condition although the example does check for duplicates).
78              Throwable t = e.getCause();
79  
80              return t != null ? t.getMessage() : e.getMessage();
81          } catch (Exception e) {
82              /*
83               * An application cannot handle any of the other exceptions raised by begin and commit so we just catch the generic
84               * exception. The meaning of the other exceptions is:
85               * 
86               * NotSupportedException - the thread is already associated with a transaction HeuristicRollbackException - should
87               * not happen since the example is interacting with a single database HeuristicMixedException - should not happen
88               * since the example is interacting with a single database SystemException - the TM raised an unexpected error.
89               * There is no standard way of handling this error (another reason why CMT are preferable to managing them
90               * ourselves)
91               */
92              return e.getMessage();
93          } finally {
94              /*
95               * Since the EM is transaction scoped it will not detach its objects even when calling close on it until the
96               * transaction completes. Therefore we must roll back any active transaction before returning.
97               */
98              try {
99                  if (userTransaction.getStatus() == Status.STATUS_ACTIVE)
100                     userTransaction.rollback();
101             } catch (Throwable e) {
102                 // ignore
103             }
104 
105             entityManager.close();
106         }
107     }
108 
109     /**
110      * Utility method for updating a key value database.
111      *
112      * @param entityManager an open JPA entity manager
113      * @param key if null or zero length then list all pairs
114      * @param value if key exists then associate value with it, otherwise create a new pair
115      * @return the new value of the key value pair or all pairs if key was null (or zero length).
116      */
117     public String updateKeyValueDatabase(EntityManager entityManager, String key, String value) {
118         StringBuilder sb = new StringBuilder();
119 
120         if (key == null || key.length() == 0) {
121             // list all key value pairs
122             @SuppressWarnings("unchecked")
123             final List<KVPair> list = entityManager.createQuery("select k from KVPair k").getResultList();
124 
125             for (KVPair kvPair : list)
126                 sb.append(kvPair.getKey()).append("=").append(kvPair.getValue()).append(',');
127 
128         } else {
129             KVPair kvPair;
130 
131             if (value == null) {
132                 // retrieve the value associated with key
133                 kvPair = new KVPair(key, value);
134 
135                 entityManager.refresh(kvPair);
136             } else {
137                 kvPair = entityManager.find(KVPair.class, key);
138 
139                 if (kvPair == null) {
140                     // insert into the key/value table
141                     kvPair = new KVPair(key, value);
142                     entityManager.persist(kvPair);
143                 } else {
144                     // update an existing row in the key/value table
145                     kvPair.setValue(value);
146                     entityManager.persist(kvPair);
147                 }
148             }
149 
150             sb.append(kvPair.getKey()).append("=").append(kvPair.getValue());
151         }
152 
153         return sb.toString();
154     }
155 }