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