1 package net.sumaris.core.dao.technical.hibernate;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 import com.google.common.collect.Multimap;
29 import net.sumaris.core.config.SumarisConfiguration;
30 import net.sumaris.core.dao.technical.Daos;
31 import net.sumaris.core.dao.technical.model.IEntity;
32 import net.sumaris.core.dao.technical.model.IUpdateDateEntityBean;
33 import net.sumaris.core.exception.BadUpdateDateException;
34 import net.sumaris.core.exception.DataLockedException;
35 import net.sumaris.core.exception.SumarisTechnicalException;
36 import net.sumaris.core.util.Dates;
37 import org.apache.commons.lang3.StringUtils;
38 import org.hibernate.Session;
39 import org.hibernate.SessionFactory;
40 import org.hibernate.dialect.Dialect;
41 import org.nuiton.i18n.I18n;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.springframework.beans.factory.annotation.Autowired;
45 import org.springframework.dao.DataAccessResourceFailureException;
46 import org.springframework.dao.DataIntegrityViolationException;
47
48 import javax.persistence.EntityManager;
49 import javax.persistence.LockModeType;
50 import javax.persistence.LockTimeoutException;
51 import javax.persistence.Query;
52 import javax.sql.DataSource;
53 import java.io.Serializable;
54 import java.math.BigInteger;
55 import java.sql.SQLException;
56 import java.sql.Timestamp;
57 import java.util.Collection;
58 import java.util.Date;
59 import java.util.Map;
60 import java.util.Objects;
61
62
63
64
65
66 public abstract class HibernateDaoSupport {
67
68
69
70
71 protected static final Logger logger =
72 LoggerFactory.getLogger(HibernateDaoSupport.class);
73
74 private boolean debugEntityLoad;
75
76 @Autowired
77 protected EntityManager entityManager;
78
79 @Autowired
80 protected SumarisConfiguration config;
81
82 @Autowired
83 private DataSource dataSource;
84
85
86
87
88 public HibernateDaoSupport() {
89 this.debugEntityLoad = SumarisConfiguration.getInstance().debugEntityLoad();
90 }
91
92
93
94
95
96
97 protected void setEntityManager(EntityManager entityManager) {
98 this.entityManager = entityManager;
99 }
100
101
102
103
104
105 @Deprecated
106 protected void setSessionFactory(SessionFactory sf) {
107 logger.warn("TODO: remove call to deprecated setSessionFactory()");
108 }
109
110
111
112
113
114
115 protected EntityManager getEntityManager() {
116 return entityManager;
117 }
118
119
120 protected Session getSession() {
121 return entityManager.unwrap(Session.class);
122 }
123
124
125
126
127
128
129 protected void deleteAll(Collection<?> entities) {
130 EntityManager entityManager = getEntityManager();
131 for (Object entity : entities) {
132 entityManager.remove(entity);
133 }
134 }
135
136
137
138
139
140
141
142 protected <T> void delete(Class<T> entityClass, Serializable identifier) {
143 EntityManager entityManager = getEntityManager();
144 T entity = entityManager.find(entityClass, identifier);
145 if (entity != null) {
146 entityManager.remove(entity);
147 }
148 }
149
150
151
152
153
154
155
156
157
158 @SuppressWarnings("unchecked")
159 protected <T> T load(Class<? extends T> clazz, Serializable id) {
160
161 if (debugEntityLoad) {
162 T load = entityManager.find(clazz, id);
163 if (load == null) {
164 throw new DataIntegrityViolationException("Unable to load entity " + clazz.getName() + " with identifier '" + id + "': not found in database.");
165 }
166 }
167 return entityManager.unwrap(Session.class).load(clazz, id);
168 }
169
170
171
172
173
174
175
176
177
178 @SuppressWarnings("unchecked")
179 protected <T> T get(Class<? extends T> clazz, Serializable id) {
180 return getEntityManager().find(clazz, id);
181 }
182
183
184
185
186
187
188
189
190
191
192 @SuppressWarnings("unchecked")
193 protected <T extends Serializable> T get(Class<? extends T> clazz, Serializable id, LockModeType lockModeType) {
194 T entity = entityManager.find(clazz, id);
195 entityManager.lock(entity, lockModeType);
196 return entity;
197 }
198
199
200
201
202
203
204
205
206
207 protected boolean executeMultipleCountWithNotNullCondition(Multimap<String, String> columnNamesByTableNames, Map<String, String> notNullConditionColumnNameByTableNames, int source) {
208
209 String countQueryString = "SELECT COUNT(*) FROM %s WHERE %s = %d";
210 return executeMultipleCount(countQueryString, columnNamesByTableNames, " AND %s IS NOT NULL", notNullConditionColumnNameByTableNames, source);
211 }
212
213
214
215
216
217
218
219
220 protected boolean executeMultipleCount(Multimap<String, String> columnNamesByTableNames, int source) {
221
222 String countQueryString = "SELECT COUNT(*) FROM %s WHERE %s = %d";
223 return executeMultipleCount(countQueryString, columnNamesByTableNames, null, null, source);
224 }
225
226
227
228
229
230
231
232
233 protected boolean executeMultipleCount(Multimap<String, String> columnNamesByTableNames, String source) {
234
235 String countQueryString = "SELECT COUNT(*) FROM %s WHERE %s = '%s'";
236 return executeMultipleCount(countQueryString, columnNamesByTableNames, null, null, source);
237 }
238
239 private boolean executeMultipleCount(
240 String countQueryString,
241 Multimap<String, String> columnNamesByTableNames,
242 String conditionQueryAppendix,
243 Map<String, String> conditionColumnNameByTableNames,
244 Object source) {
245
246 String queryString;
247 Query query;
248 for (String tableName : columnNamesByTableNames.keySet()) {
249 Collection<String> columnNames = columnNamesByTableNames.get(tableName);
250 String conditionColumnName = conditionColumnNameByTableNames == null ? null : conditionColumnNameByTableNames.get(tableName);
251 for (String columnName : columnNames) {
252 if (StringUtils.isNotBlank(conditionQueryAppendix) && StringUtils.isNotBlank(conditionColumnName)) {
253 queryString = String.format(countQueryString.concat(conditionQueryAppendix), tableName, columnName, source, conditionColumnName);
254 } else {
255 queryString = String.format(countQueryString, tableName, columnName, source);
256 }
257
258 query = entityManager.createNativeQuery(queryString);
259 if (logger.isDebugEnabled()) {
260 logger.debug(queryString);
261 }
262 BigInteger count = (BigInteger) query.getSingleResult();
263 if (count.intValue() > 0) {
264 return true;
265 }
266 }
267 }
268 return false;
269 }
270
271
272
273
274
275
276
277
278 protected void executeMultipleUpdate(Multimap<String, String> columnNamesByTableNames, int sourceId, int targetId) {
279
280 String updateQueryString = "UPDATE %s SET %s = %d WHERE %s = %d";
281 executeMultipleUpdate(updateQueryString, columnNamesByTableNames, null, null, sourceId, targetId);
282 }
283
284
285
286
287
288
289
290
291
292 protected void executeMultipleUpdateWithNullCondition(Multimap<String, String> columnNamesByTableNames, Map<String, String> nullConditionColumnNameByTableNames, int sourceId, int targetId) {
293
294 String updateQueryString = "UPDATE %s SET %s = %d WHERE %s = %d";
295 executeMultipleUpdate(updateQueryString, columnNamesByTableNames, " AND %s IS NULL", nullConditionColumnNameByTableNames, sourceId, targetId);
296 }
297
298
299
300
301
302
303
304
305 protected void executeMultipleUpdate(Multimap<String, String> columnNamesByTableNames, String sourceCode, String targetCode) {
306
307 String updateQueryString = "UPDATE %s SET %s = '%s' WHERE %s = '%s'";
308 executeMultipleUpdate(updateQueryString, columnNamesByTableNames, null, null, sourceCode, targetCode);
309 }
310
311 private void executeMultipleUpdate(
312 String updateQueryString,
313 Multimap<String, String> columnNamesByTableNames,
314 String conditionQueryAppendix,
315 Map<String, String> conditionColumnNameByTableNames,
316 Object source,
317 Object target) {
318
319 String queryString;
320 Query query;
321
322 for (String tableName : columnNamesByTableNames.keySet()) {
323 Collection<String> columnNames = columnNamesByTableNames.get(tableName);
324 String conditionColumnName = conditionColumnNameByTableNames == null ? null : conditionColumnNameByTableNames.get(tableName);
325 for (String columnName : columnNames) {
326 if (StringUtils.isNotBlank(conditionQueryAppendix) && StringUtils.isNotBlank(conditionColumnName)) {
327 queryString = String.format(updateQueryString.concat(conditionQueryAppendix), tableName, columnName, target, columnName, source, conditionColumnName);
328 } else {
329 queryString = String.format(updateQueryString, tableName, columnName, target, columnName, source);
330 }
331
332 query = entityManager.createNativeQuery(queryString);
333 if (logger.isDebugEnabled()) {
334 logger.debug(queryString);
335 }
336 query.executeUpdate();
337 }
338 }
339 }
340
341
342
343
344
345
346 protected Timestamp getDatabaseCurrentTimestamp() {
347 try {
348 final Dialect dialect = Dialect.getDialect(SumarisConfiguration.getInstance().getConnectionProperties());
349 final String sql = dialect.getCurrentTimestampSelectString();
350 Object r = Daos.sqlUniqueTimestamp(dataSource, sql);
351 return Daos.toTimestampFromJdbcResult(r);
352 }catch(DataAccessResourceFailureException | SQLException e) {
353 throw new SumarisTechnicalException(e);
354 }
355 }
356
357
358 protected String getTableName(String entityName) {
359
360 return I18n.t("sumaris.persistence.table."+ entityName.substring(0,1).toLowerCase() + entityName.substring(1));
361 }
362
363 protected void checkUpdateDateForUpdate(IUpdateDateEntityBean<?, ? extends Date> source,
364 IUpdateDateEntityBean<?, ? extends Date> entity) {
365
366 if (entity.getUpdateDate() != null) {
367 Timestamp serverUpdateDtNoMillisecond = Dates.resetMillisecond(entity.getUpdateDate());
368 Timestamp sourceUpdateDtNoMillisecond = Dates.resetMillisecond(source.getUpdateDate());
369 if (!Objects.equals(sourceUpdateDtNoMillisecond, serverUpdateDtNoMillisecond)) {
370 throw new BadUpdateDateException(I18n.t("sumaris.persistence.error.badUpdateDate",
371 getTableName(entity.getClass().getSimpleName()), source.getId(), serverUpdateDtNoMillisecond,
372 sourceUpdateDtNoMillisecond));
373 }
374 }
375 }
376
377 protected void lockForUpdate(IEntity<?> entity) {
378 lockForUpdate(entity, LockModeType.PESSIMISTIC_WRITE);
379 }
380
381 protected void lockForUpdate(IEntity<?> entity, LockModeType modeType) {
382
383 try {
384 entityManager.lock(entity, modeType);
385 } catch (LockTimeoutException e) {
386 throw new DataLockedException(I18n.t("sumaris.persistence.error.locked",
387 getTableName(entity.getClass().getSimpleName()), entity.getId()), e);
388 }
389 }
390
391 protected void delete(IEntity<?> entity) {
392 entityManager.remove(entity);
393 }
394
395 }