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 }