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 }