001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.service.persistence.impl;
016    
017    import com.liferay.portal.NoSuchModelException;
018    import com.liferay.portal.kernel.configuration.Filter;
019    import com.liferay.portal.kernel.dao.db.DB;
020    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
021    import com.liferay.portal.kernel.dao.orm.Dialect;
022    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
023    import com.liferay.portal.kernel.dao.orm.ORMException;
024    import com.liferay.portal.kernel.dao.orm.OrderFactoryUtil;
025    import com.liferay.portal.kernel.dao.orm.Projection;
026    import com.liferay.portal.kernel.dao.orm.ProjectionFactoryUtil;
027    import com.liferay.portal.kernel.dao.orm.Session;
028    import com.liferay.portal.kernel.dao.orm.SessionFactory;
029    import com.liferay.portal.kernel.exception.SystemException;
030    import com.liferay.portal.kernel.log.Log;
031    import com.liferay.portal.kernel.log.LogFactoryUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.NullSafeStringComparator;
034    import com.liferay.portal.kernel.util.OrderByComparator;
035    import com.liferay.portal.kernel.util.PropsKeys;
036    import com.liferay.portal.kernel.util.PropsUtil;
037    import com.liferay.portal.kernel.util.StringBundler;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.model.BaseModel;
040    import com.liferay.portal.model.ModelListener;
041    import com.liferay.portal.model.ModelListenerRegistrationUtil;
042    import com.liferay.portal.model.ModelWrapper;
043    import com.liferay.portal.service.ServiceContext;
044    import com.liferay.portal.service.ServiceContextThreadLocal;
045    import com.liferay.portal.service.persistence.BasePersistence;
046    
047    import java.io.Serializable;
048    
049    import java.sql.Connection;
050    import java.sql.Types;
051    
052    import java.util.Collections;
053    import java.util.Comparator;
054    import java.util.List;
055    import java.util.Map;
056    import java.util.Set;
057    
058    import javax.sql.DataSource;
059    
060    /**
061     * The base implementation for all persistence classes. This class should never
062     * need to be used directly.
063     *
064     * <p>
065     * Caching information and settings can be found in
066     * <code>portal.properties</code>
067     * </p>
068     *
069     * @author Brian Wing Shun Chan
070     * @author Shuyang Zhou
071     * @author Peter Fellwock
072     */
073    public class BasePersistenceImpl<T extends BaseModel<T>>
074            implements BasePersistence<T>, SessionFactory {
075    
076            public static final String COUNT_COLUMN_NAME = "COUNT_VALUE";
077    
078            @Override
079            public void clearCache() {
080            }
081    
082            @Override
083            public void clearCache(List<T> model) {
084            }
085    
086            @Override
087            public void clearCache(T model) {
088            }
089    
090            @Override
091            public void closeSession(Session session) {
092                    _sessionFactory.closeSession(session);
093            }
094    
095            @Override
096            public long countWithDynamicQuery(DynamicQuery dynamicQuery) {
097                    return countWithDynamicQuery(
098                            dynamicQuery, ProjectionFactoryUtil.rowCount());
099            }
100    
101            @Override
102            public long countWithDynamicQuery(
103                    DynamicQuery dynamicQuery, Projection projection) {
104    
105                    if (projection == null) {
106                            projection = ProjectionFactoryUtil.rowCount();
107                    }
108    
109                    dynamicQuery.setProjection(projection);
110    
111                    List<Long> results = findWithDynamicQuery(dynamicQuery);
112    
113                    if (results.isEmpty()) {
114                            return 0;
115                    }
116                    else {
117                            return (results.get(0)).longValue();
118                    }
119            }
120    
121            @Override
122            public T fetchByPrimaryKey(Serializable primaryKey) {
123                    throw new UnsupportedOperationException();
124            }
125    
126            @Override
127            public Map<Serializable, T> fetchByPrimaryKeys(
128                    Set<Serializable> primaryKeys) {
129    
130                    throw new UnsupportedOperationException();
131            }
132    
133            @Override
134            @SuppressWarnings("unused")
135            public T findByPrimaryKey(Serializable primaryKey)
136                    throws NoSuchModelException {
137    
138                    throw new UnsupportedOperationException();
139            }
140    
141            @Override
142            public <V> List<V> findWithDynamicQuery(DynamicQuery dynamicQuery) {
143                    Session session = null;
144    
145                    try {
146                            session = openSession();
147    
148                            dynamicQuery.compile(session);
149    
150                            return dynamicQuery.list();
151                    }
152                    catch (Exception e) {
153                            throw processException(e);
154                    }
155                    finally {
156                            closeSession(session);
157                    }
158            }
159    
160            @Override
161            public <V> List<V> findWithDynamicQuery(
162                    DynamicQuery dynamicQuery, int start, int end) {
163    
164                    Session session = null;
165    
166                    try {
167                            session = openSession();
168    
169                            dynamicQuery.setLimit(start, end);
170    
171                            dynamicQuery.compile(session);
172    
173                            return dynamicQuery.list();
174                    }
175                    catch (Exception e) {
176                            throw processException(e);
177                    }
178                    finally {
179                            closeSession(session);
180                    }
181            }
182    
183            @Override
184            public <V> List<V> findWithDynamicQuery(
185                    DynamicQuery dynamicQuery, int start, int end,
186                    OrderByComparator<V> orderByComparator) {
187    
188                    OrderFactoryUtil.addOrderByComparator(dynamicQuery, orderByComparator);
189    
190                    return findWithDynamicQuery(dynamicQuery, start, end);
191            }
192    
193            @Override
194            public void flush() {
195                    try {
196                            Session session = _sessionFactory.getCurrentSession();
197    
198                            if (session != null) {
199                                    session.flush();
200                            }
201                    }
202                    catch (Exception e) {
203                            throw processException(e);
204                    }
205            }
206    
207            @Override
208            public Set<String> getBadColumnNames() {
209                    return Collections.emptySet();
210            }
211    
212            @Override
213            public Session getCurrentSession() throws ORMException {
214                    return _sessionFactory.getCurrentSession();
215            }
216    
217            @Override
218            public DataSource getDataSource() {
219                    return _dataSource;
220            }
221    
222            public DB getDB() {
223                    return _db;
224            }
225    
226            @Override
227            public Dialect getDialect() {
228                    return _dialect;
229            }
230    
231            @Override
232            public ModelListener<T>[] getListeners() {
233                    return ModelListenerRegistrationUtil.getModelListeners(getModelClass());
234            }
235    
236            @Override
237            public Class<T> getModelClass() {
238                    return _modelClass;
239            }
240    
241            @Override
242            public Session openNewSession(Connection connection) throws ORMException {
243                    return _sessionFactory.openNewSession(connection);
244            }
245    
246            @Override
247            public Session openSession() throws ORMException {
248                    return _sessionFactory.openSession();
249            }
250    
251            @Override
252            public SystemException processException(Exception e) {
253                    if (!(e instanceof ORMException)) {
254                            _log.error("Caught unexpected exception " + e.getClass().getName());
255                    }
256    
257                    if (_log.isDebugEnabled()) {
258                            _log.debug(e, e);
259                    }
260    
261                    return new SystemException(e);
262            }
263    
264            @Override
265            public void registerListener(ModelListener<T> listener) {
266                    ModelListenerRegistrationUtil.register(listener);
267            }
268    
269            @Override
270            @SuppressWarnings("unused")
271            public T remove(Serializable primaryKey) throws NoSuchModelException {
272                    throw new UnsupportedOperationException();
273            }
274    
275            @Override
276            public T remove(T model) {
277                    if (model instanceof ModelWrapper) {
278                            ModelWrapper<T> modelWrapper = (ModelWrapper<T>)model;
279    
280                            model = modelWrapper.getWrappedModel();
281                    }
282    
283                    ModelListener<T>[] listeners = getListeners();
284    
285                    for (ModelListener<T> listener : listeners) {
286                            listener.onBeforeRemove(model);
287                    }
288    
289                    model = removeImpl(model);
290    
291                    for (ModelListener<T> listener : listeners) {
292                            listener.onAfterRemove(model);
293                    }
294    
295                    return model;
296            }
297    
298            @Override
299            public void setDataSource(DataSource dataSource) {
300                    _dataSource = dataSource;
301            }
302    
303            public void setSessionFactory(SessionFactory sessionFactory) {
304                    _sessionFactory = sessionFactory;
305                    _dialect = _sessionFactory.getDialect();
306                    _db = DBFactoryUtil.getDB(_dialect, getDataSource());
307    
308                    _databaseOrderByMaxColumns = GetterUtil.getInteger(
309                            PropsUtil.get(
310                                    PropsKeys.DATABASE_ORDER_BY_MAX_COLUMNS,
311                                    new Filter(_db.getType())));
312            }
313    
314            @Override
315            public void unregisterListener(ModelListener<T> listener) {
316                    ModelListenerRegistrationUtil.unregister(listener);
317            }
318    
319            @Override
320            public T update(T model) {
321                    if (model instanceof ModelWrapper) {
322                            ModelWrapper<T> modelWrapper = (ModelWrapper<T>)model;
323    
324                            model = modelWrapper.getWrappedModel();
325                    }
326    
327                    boolean isNew = model.isNew();
328    
329                    ModelListener<T>[] listeners = getListeners();
330    
331                    for (ModelListener<T> listener : listeners) {
332                            if (isNew) {
333                                    listener.onBeforeCreate(model);
334                            }
335                            else {
336                                    listener.onBeforeUpdate(model);
337                            }
338                    }
339    
340                    model = updateImpl(model);
341    
342                    for (ModelListener<T> listener : listeners) {
343                            if (isNew) {
344                                    listener.onAfterCreate(model);
345                            }
346                            else {
347                                    listener.onAfterUpdate(model);
348                            }
349                    }
350    
351                    return model;
352            }
353    
354            /**
355             * @deprecated As of 6.2.0, replaced by {@link #update(BaseModel)}}
356             */
357            @Deprecated
358            @Override
359            public T update(T model, boolean merge) {
360                    if (model instanceof ModelWrapper) {
361                            ModelWrapper<T> modelWrapper = (ModelWrapper<T>)model;
362    
363                            model = modelWrapper.getWrappedModel();
364                    }
365    
366                    boolean isNew = model.isNew();
367    
368                    ModelListener<T>[] listeners = getListeners();
369    
370                    for (ModelListener<T> listener : listeners) {
371                            if (isNew) {
372                                    listener.onBeforeCreate(model);
373                            }
374                            else {
375                                    listener.onBeforeUpdate(model);
376                            }
377                    }
378    
379                    model = updateImpl(model, merge);
380    
381                    for (ModelListener<T> listener : listeners) {
382                            if (isNew) {
383                                    listener.onAfterCreate(model);
384                            }
385                            else {
386                                    listener.onAfterUpdate(model);
387                            }
388                    }
389    
390                    return model;
391            }
392    
393            /**
394             * @deprecated As of 6.2.0, replaced by {@link #update(BaseModel,
395             *             ServiceContext)}}
396             */
397            @Deprecated
398            @Override
399            public T update(T model, boolean merge, ServiceContext serviceContext) {
400                    return update(model, serviceContext);
401            }
402    
403            @Override
404            public T update(T model, ServiceContext serviceContext) {
405                    try {
406                            ServiceContextThreadLocal.pushServiceContext(serviceContext);
407    
408                            update(model);
409    
410                            return model;
411                    }
412                    finally {
413                            ServiceContextThreadLocal.popServiceContext();
414                    }
415            }
416    
417            protected static String removeConjunction(String sql) {
418                    int pos = sql.indexOf(" AND ");
419    
420                    if (pos != -1) {
421                            sql = sql.substring(0, pos);
422                    }
423    
424                    return sql;
425            }
426    
427            protected void appendOrderByComparator(
428                    StringBundler query, String entityAlias,
429                    OrderByComparator<T> orderByComparator) {
430    
431                    appendOrderByComparator(query, entityAlias, orderByComparator, false);
432            }
433    
434            protected void appendOrderByComparator(
435                    StringBundler query, String entityAlias,
436                    OrderByComparator<T> orderByComparator, boolean sqlQuery) {
437    
438                    query.append(ORDER_BY_CLAUSE);
439    
440                    String[] orderByFields = orderByComparator.getOrderByFields();
441    
442                    int length = orderByFields.length;
443    
444                    if ((_databaseOrderByMaxColumns > 0) &&
445                            (_databaseOrderByMaxColumns < length)) {
446    
447                            length = _databaseOrderByMaxColumns;
448                    }
449    
450                    for (int i = 0; i < length; i++) {
451                            query.append(
452                                    getColumnName(entityAlias, orderByFields[i], sqlQuery));
453    
454                            if ((i + 1) < length) {
455                                    if (orderByComparator.isAscending(orderByFields[i])) {
456                                            query.append(ORDER_BY_ASC_HAS_NEXT);
457                                    }
458                                    else {
459                                            query.append(ORDER_BY_DESC_HAS_NEXT);
460                                    }
461                            }
462                            else {
463                                    if (orderByComparator.isAscending(orderByFields[i])) {
464                                            query.append(ORDER_BY_ASC);
465                                    }
466                                    else {
467                                            query.append(ORDER_BY_DESC);
468                                    }
469                            }
470                    }
471            }
472    
473            protected ClassLoader getClassLoader() {
474                    Class<?> clazz = getClass();
475    
476                    return clazz.getClassLoader();
477            }
478    
479            protected String getColumnName(
480                    String entityAlias, String fieldName, boolean sqlQuery) {
481    
482                    String columnName = fieldName;
483    
484                    Set<String> badColumnNames = getBadColumnNames();
485    
486                    if (badColumnNames.contains(fieldName)) {
487                            columnName = columnName.concat(StringPool.UNDERLINE);
488                    }
489    
490                    if (sqlQuery) {
491                            fieldName = columnName;
492                    }
493    
494                    fieldName = entityAlias.concat(fieldName);
495    
496                    Map<String, Integer> tableColumnsMap = getTableColumnsMap();
497    
498                    Integer type = tableColumnsMap.get(columnName);
499    
500                    if (type == null) {
501                            throw new IllegalArgumentException(
502                                    "Unknown column name " + columnName);
503                    }
504    
505                    if (type == Types.CLOB) {
506                            fieldName = CAST_CLOB_TEXT_OPEN.concat(fieldName).concat(
507                                    StringPool.CLOSE_PARENTHESIS);
508                    }
509    
510                    return fieldName;
511            }
512    
513            protected Map<String, Integer> getTableColumnsMap() {
514                    throw new UnsupportedOperationException();
515            }
516    
517            /**
518             * Removes the model instance from the database. {@link #update(BaseModel,
519             * boolean)} depends on this method to implement the remove operation; it
520             * only notifies the model listeners.
521             *
522             * @param  model the model instance to remove
523             * @return the model instance that was removed
524             */
525            protected T removeImpl(T model) {
526                    throw new UnsupportedOperationException();
527            }
528    
529            protected void setModelClass(Class<T> modelClass) {
530                    _modelClass = modelClass;
531            }
532    
533            /**
534             * Updates the model instance in the database or adds it if it does not yet
535             * exist. {@link #remove(BaseModel)} depends on this method to implement the
536             * update operation; it only notifies the model listeners.
537             *
538             * @param  model the model instance to update
539             * @return the model instance that was updated
540             */
541            protected T updateImpl(T model) {
542                    throw new UnsupportedOperationException();
543            }
544    
545            /**
546             * @deprecated As of 6.2.0, replaced by {@link #updateImpl(BaseModel)}
547             */
548            @Deprecated
549            protected T updateImpl(T model, boolean merge) {
550                    return updateImpl(model);
551            }
552    
553            protected static final String CAST_CLOB_TEXT_OPEN = "CAST_CLOB_TEXT(";
554    
555            protected static final Object[] FINDER_ARGS_EMPTY = new Object[0];
556    
557            protected static final Comparator<String> NULL_SAFE_STRING_COMPARATOR =
558                    new NullSafeStringComparator();
559    
560            protected static final String ORDER_BY_ASC = " ASC";
561    
562            protected static final String ORDER_BY_ASC_HAS_NEXT = " ASC, ";
563    
564            protected static final String ORDER_BY_CLAUSE = " ORDER BY ";
565    
566            protected static final String ORDER_BY_DESC = " DESC";
567    
568            protected static final String ORDER_BY_DESC_HAS_NEXT = " DESC, ";
569    
570            protected static final String WHERE_AND = " AND ";
571    
572            protected static final String WHERE_GREATER_THAN = " >= ? ";
573    
574            protected static final String WHERE_GREATER_THAN_HAS_NEXT = " >= ? AND ";
575    
576            protected static final String WHERE_LESSER_THAN = " <= ? ";
577    
578            protected static final String WHERE_LESSER_THAN_HAS_NEXT = " <= ? AND ";
579    
580            protected static final String WHERE_OR = " OR ";
581    
582            /**
583             * @deprecated As of 7.0.0, with no direct replacement
584             */
585            @Deprecated
586            protected ModelListener<T>[] listeners = new ModelListener[0];
587    
588            private static final Log _log = LogFactoryUtil.getLog(
589                    BasePersistenceImpl.class);
590    
591            private int _databaseOrderByMaxColumns;
592            private DataSource _dataSource;
593            private DB _db;
594            private Dialect _dialect;
595            private Class<T> _modelClass;
596            private SessionFactory _sessionFactory;
597    
598    }