001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2011 SonarSource
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 }