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.close(); 059 entityManager = null; 060 } 061 } 062 063 public void commit() { 064 if (entityManager != null && inTransaction) { 065 if (entityManager.isOpen()) { 066 if (entityManager.getTransaction().getRollbackOnly()) { 067 entityManager.getTransaction().rollback(); 068 } else { 069 entityManager.getTransaction().commit(); 070 } 071 entityManager.clear(); 072 index = 0; 073 } 074 inTransaction = false; 075 } 076 } 077 078 public void rollback() { 079 if (entityManager != null && inTransaction) { 080 entityManager.getTransaction().rollback(); 081 inTransaction = false; 082 } 083 } 084 085 public <T> T save(T model) { 086 startTransaction(); 087 internalSave(model, true); 088 return model; 089 } 090 091 public Object saveWithoutFlush(Object model) { 092 startTransaction(); 093 internalSave(model, false); 094 return model; 095 } 096 097 public boolean contains(Object model) { 098 startTransaction(); 099 return entityManager.contains(model); 100 } 101 102 public void save(Object... models) { 103 startTransaction(); 104 for (Object model : models) { 105 save(model); 106 } 107 } 108 109 private void internalSave(Object model, boolean flushIfNeeded) { 110 entityManager.persist(model); 111 if (flushIfNeeded && (++index % BATCH_SIZE == 0)) { 112 commit(); 113 } 114 } 115 116 public Object merge(Object model) { 117 startTransaction(); 118 return entityManager.merge(model); 119 } 120 121 public void remove(Object model) { 122 startTransaction(); 123 entityManager.remove(model); 124 if (++index % BATCH_SIZE == 0) { 125 commit(); 126 } 127 } 128 129 public void removeWithoutFlush(Object model) { 130 startTransaction(); 131 entityManager.remove(model); 132 } 133 134 public <T> T reattach(Class<T> entityClass, Object primaryKey) { 135 startTransaction(); 136 return entityManager.getReference(entityClass, primaryKey); 137 } 138 139 private void startTransaction() { 140 if (!inTransaction) { 141 entityManager.getTransaction().begin(); 142 inTransaction = true; 143 } 144 } 145 146 147 public Query createQuery(String hql) { 148 startTransaction(); 149 return entityManager.createQuery(hql); 150 } 151 152 public <T> T getSingleResult(Query query, T defaultValue) { 153 try { 154 return (T) query.getSingleResult(); 155 } catch (NoResultException ex) { 156 return defaultValue; 157 } 158 } 159 160 public <T> T getEntity(Class<T> entityClass, Object id) { 161 startTransaction(); 162 return getEntityManager().find(entityClass, id); 163 } 164 165 public <T> T getSingleResult(Class<T> entityClass, Object... criterias) { 166 try { 167 return getSingleResult(getQueryForCriterias(entityClass, true, criterias), (T) null); 168 169 } catch (NonUniqueResultException ex) { 170 LoggerFactory.getLogger(JpaDatabaseSession.class).warn("NonUniqueResultException on entity {} with criterias : {}", 171 entityClass.getSimpleName(), StringUtils.join(criterias, ",")); 172 throw ex; 173 } 174 } 175 176 public <T> List<T> getResults(Class<T> entityClass, Object... criterias) { 177 return getQueryForCriterias(entityClass, true, criterias).getResultList(); 178 } 179 180 public <T> List<T> getResults(Class<T> entityClass) { 181 return getQueryForCriterias(entityClass, false, null).getResultList(); 182 } 183 184 private Query getQueryForCriterias(Class<?> entityClass, boolean raiseError, Object... criterias) { 185 if (criterias == null && raiseError) { 186 throw new IllegalStateException("criterias parameter must be provided"); 187 } 188 startTransaction(); 189 StringBuilder hql = new StringBuilder("SELECT o FROM ").append(entityClass.getSimpleName()).append(" o"); 190 if (criterias != null) { 191 hql.append(" WHERE "); 192 Map<String, Object> mappedCriterias = new HashMap<String, Object>(); 193 for (int i = 0; i < criterias.length; i += 2) { 194 mappedCriterias.put((String) criterias[i], criterias[i + 1]); 195 } 196 buildCriteriasHQL(hql, mappedCriterias); 197 Query query = getEntityManager().createQuery(hql.toString()); 198 199 for (Map.Entry<String, Object> entry : mappedCriterias.entrySet()) { 200 query.setParameter(entry.getKey(), entry.getValue()); 201 } 202 return query; 203 } 204 return getEntityManager().createQuery(hql.toString()); 205 } 206 207 private void buildCriteriasHQL(StringBuilder hql, Map<String, Object> mappedCriterias) { 208 for (Iterator<String> i = mappedCriterias.keySet().iterator(); i.hasNext();) { 209 String criteria = i.next(); 210 hql.append("o.").append(criteria).append("=:").append(criteria); 211 if (i.hasNext()) { 212 hql.append(" AND "); 213 } 214 } 215 } 216 217 }