001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.jpa.session; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.slf4j.LoggerFactory; 024 import org.sonar.api.database.DatabaseSession; 025 026 import javax.persistence.EntityManager; 027 import javax.persistence.NoResultException; 028 import javax.persistence.NonUniqueResultException; 029 import javax.persistence.Query; 030 import java.util.HashMap; 031 import java.util.Iterator; 032 import java.util.List; 033 import java.util.Map; 034 035 public class JpaDatabaseSession extends DatabaseSession { 036 037 private final DatabaseConnector connector; 038 private EntityManager entityManager = null; 039 private int index = 0; 040 private boolean inTransaction = false; 041 042 public JpaDatabaseSession(DatabaseConnector connector) { 043 this.connector = connector; 044 } 045 046 public EntityManager getEntityManager() { 047 return entityManager; 048 } 049 050 public void start() { 051 entityManager = connector.createEntityManager(); 052 index = 0; 053 } 054 055 public void stop() { 056 commit(); 057 if (entityManager != null && entityManager.isOpen()) { 058 entityManager.clear(); 059 entityManager.close(); 060 entityManager = null; 061 } 062 } 063 064 public void commit() { 065 if (entityManager != null && inTransaction) { 066 if (entityManager.isOpen()) { 067 if (entityManager.getTransaction().getRollbackOnly()) { 068 entityManager.getTransaction().rollback(); 069 } else { 070 entityManager.getTransaction().commit(); 071 } 072 } 073 inTransaction = false; 074 index = 0; 075 } 076 } 077 078 public void rollback() { 079 if (entityManager != null && inTransaction) { 080 entityManager.getTransaction().rollback(); 081 inTransaction = false; 082 index = 0; 083 } 084 } 085 086 public <T> T save(T model) { 087 startTransaction(); 088 internalSave(model, true); 089 return model; 090 } 091 092 public Object saveWithoutFlush(Object model) { 093 startTransaction(); 094 internalSave(model, false); 095 return model; 096 } 097 098 public boolean contains(Object model) { 099 startTransaction(); 100 return entityManager.contains(model); 101 } 102 103 public void save(Object... models) { 104 startTransaction(); 105 for (Object model : models) { 106 save(model); 107 } 108 } 109 110 private void internalSave(Object model, boolean flushIfNeeded) { 111 entityManager.persist(model); 112 if (flushIfNeeded && (++index % BATCH_SIZE == 0)) { 113 flush(); 114 } 115 } 116 117 public Object merge(Object model) { 118 startTransaction(); 119 return entityManager.merge(model); 120 } 121 122 public void remove(Object model) { 123 startTransaction(); 124 entityManager.remove(model); 125 if (++index % BATCH_SIZE == 0) { 126 flush(); 127 } 128 } 129 130 public void removeWithoutFlush(Object model) { 131 startTransaction(); 132 entityManager.remove(model); 133 } 134 135 public <T> T reattach(Class<T> entityClass, Object primaryKey) { 136 startTransaction(); 137 return entityManager.getReference(entityClass, primaryKey); 138 } 139 140 private void startTransaction() { 141 if (!inTransaction) { 142 entityManager.getTransaction().begin(); 143 inTransaction = true; 144 } 145 } 146 147 public void flush() { 148 entityManager.flush(); 149 entityManager.clear(); 150 } 151 152 public Query createQuery(String hql) { 153 startTransaction(); 154 return entityManager.createQuery(hql); 155 } 156 157 public <T> T getSingleResult(Query query, T defaultValue) { 158 try { 159 return (T) query.getSingleResult(); 160 } catch (NoResultException ex) { 161 return defaultValue; 162 } 163 } 164 165 public <T> T getEntity(Class<T> entityClass, Object id) { 166 startTransaction(); 167 return getEntityManager().find(entityClass, id); 168 } 169 170 public <T> T getSingleResult(Class<T> entityClass, Object... criterias) { 171 try { 172 return getSingleResult(getQueryForCriterias(entityClass, true, criterias), (T) null); 173 174 } catch (NonUniqueResultException ex) { 175 LoggerFactory.getLogger(JpaDatabaseSession.class).warn("NonUniqueResultException on entity {} with criterias : {}", 176 entityClass.getSimpleName(), StringUtils.join(criterias, ",")); 177 throw ex; 178 } 179 } 180 181 public <T> List<T> getResults(Class<T> entityClass, Object... criterias) { 182 return getQueryForCriterias(entityClass, true, criterias).getResultList(); 183 } 184 185 public <T> List<T> getResults(Class<T> entityClass) { 186 return getQueryForCriterias(entityClass, false, null).getResultList(); 187 } 188 189 private Query getQueryForCriterias(Class<?> entityClass, boolean raiseError, Object... criterias) { 190 if (criterias == null && raiseError) { 191 throw new IllegalStateException("criterias parameter must be provided"); 192 } 193 startTransaction(); 194 StringBuilder hql = new StringBuilder("SELECT o FROM ").append(entityClass.getSimpleName()).append(" o"); 195 if (criterias != null) { 196 hql.append(" WHERE "); 197 Map<String, Object> mappedCriterias = new HashMap<String, Object>(); 198 for (int i = 0; i < criterias.length; i += 2) { 199 mappedCriterias.put((String) criterias[i], criterias[i + 1]); 200 } 201 buildCriteriasHQL(hql, mappedCriterias); 202 Query query = getEntityManager().createQuery(hql.toString()); 203 204 for (Map.Entry<String, Object> entry : mappedCriterias.entrySet()) { 205 query.setParameter(entry.getKey(), entry.getValue()); 206 } 207 return query; 208 } 209 return getEntityManager().createQuery(hql.toString()); 210 } 211 212 private void buildCriteriasHQL(StringBuilder hql, Map<String, Object> mappedCriterias) { 213 for (Iterator<String> i = mappedCriterias.keySet().iterator(); i.hasNext();) { 214 String criteria = i.next(); 215 hql.append("o.").append(criteria).append("=:").append(criteria); 216 if (i.hasNext()) { 217 hql.append(" AND "); 218 } 219 } 220 } 221 222 }