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.cache.MultiVMPoolUtil;
019    import com.liferay.portal.kernel.cache.PortalCache;
020    import com.liferay.portal.kernel.cache.PortalCacheHelperUtil;
021    import com.liferay.portal.kernel.dao.jdbc.MappingSqlQuery;
022    import com.liferay.portal.kernel.dao.jdbc.MappingSqlQueryFactoryUtil;
023    import com.liferay.portal.kernel.dao.jdbc.RowMapper;
024    import com.liferay.portal.kernel.dao.jdbc.SqlUpdate;
025    import com.liferay.portal.kernel.dao.jdbc.SqlUpdateFactoryUtil;
026    import com.liferay.portal.kernel.exception.SystemException;
027    import com.liferay.portal.kernel.util.ListUtil;
028    import com.liferay.portal.kernel.util.OrderByComparator;
029    import com.liferay.portal.model.BaseModel;
030    import com.liferay.portal.model.ModelListener;
031    import com.liferay.portal.service.persistence.BasePersistence;
032    
033    import java.sql.Types;
034    
035    import java.util.ArrayList;
036    import java.util.Arrays;
037    import java.util.Collections;
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Map.Entry;
042    import java.util.Set;
043    import java.util.concurrent.ConcurrentHashMap;
044    
045    import javax.sql.DataSource;
046    
047    /**
048     * @author Shuyang Zhou
049     */
050    public class TableMapperImpl<L extends BaseModel<L>, R extends BaseModel<R>>
051            implements TableMapper<L, R> {
052    
053            public TableMapperImpl(
054                    String tableName, String companyColumnName, String leftColumnName,
055                    String rightColumnName, BasePersistence<L> leftBasePersistence,
056                    BasePersistence<R> rightBasePersistence) {
057    
058                    this.tableName = tableName;
059                    this.companyColumnName = companyColumnName;
060                    this.leftColumnName = leftColumnName;
061                    this.rightColumnName = rightColumnName;
062                    this.leftBasePersistence = leftBasePersistence;
063                    this.rightBasePersistence = rightBasePersistence;
064    
065                    DataSource dataSource = leftBasePersistence.getDataSource();
066    
067                    addTableMappingSqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(
068                            dataSource,
069                            "INSERT INTO " + tableName + " (" + companyColumnName + ", " +
070                                    leftColumnName + ", " + rightColumnName + ") VALUES (?, ?, ?)",
071                            new int[] {Types.BIGINT, Types.BIGINT, Types.BIGINT});
072                    deleteLeftPrimaryKeyTableMappingsSqlUpdate =
073                            SqlUpdateFactoryUtil.getSqlUpdate(
074                                    dataSource,
075                                    "DELETE FROM " + tableName + " WHERE " + companyColumnName +
076                                            " = ? AND " +leftColumnName + " = ?",
077                                    new int[] {Types.BIGINT, Types.BIGINT});
078                    deleteRightPrimaryKeyTableMappingsSqlUpdate =
079                            SqlUpdateFactoryUtil.getSqlUpdate(
080                                    dataSource,
081                                    "DELETE FROM " + tableName + " WHERE " + companyColumnName +
082                                            " = ? AND " + rightColumnName + " = ?",
083                                    new int[] {Types.BIGINT, Types.BIGINT});
084                    deleteTableMappingSqlUpdate = SqlUpdateFactoryUtil.getSqlUpdate(
085                            dataSource,
086                            "DELETE FROM " + tableName + " WHERE " + companyColumnName +
087                                    " = ? AND " + leftColumnName + " = ? AND " + rightColumnName +
088                                            " = ?",
089                            new int[] {Types.BIGINT, Types.BIGINT, Types.BIGINT});
090                    getLeftPrimaryKeysSqlQuery =
091                            MappingSqlQueryFactoryUtil.getMappingSqlQuery(
092                                    dataSource,
093                                    "SELECT " + leftColumnName + " FROM " + tableName + " WHERE " +
094                                            companyColumnName + " = ? AND " + rightColumnName + " = ?",
095                                    new int[] {Types.BIGINT, Types.BIGINT}, RowMapper.PRIMARY_KEY);
096                    getRightPrimaryKeysSqlQuery =
097                            MappingSqlQueryFactoryUtil.getMappingSqlQuery(
098                                    dataSource,
099                                    "SELECT " + rightColumnName + " FROM " + tableName + " WHERE " +
100                                            companyColumnName + " = ? AND " + leftColumnName + " = ?",
101                                    new int[] {Types.BIGINT, Types.BIGINT}, RowMapper.PRIMARY_KEY);
102            }
103    
104            @Override
105            public boolean addTableMapping(
106                    long companyId, long leftPrimaryKey, long rightPrimaryKey) {
107    
108                    if (containsTableMapping(
109                                    companyId, leftPrimaryKey, rightPrimaryKey, false)) {
110    
111                            return false;
112                    }
113    
114                    PortalCache<Long, long[]> leftToRightPortalCache =
115                            getLeftToRightPortalCache(companyId);
116    
117                    leftToRightPortalCache.remove(leftPrimaryKey);
118    
119                    PortalCache<Long, long[]> rightToLeftPortalCache =
120                            getRightToLeftPortalCache(companyId);
121    
122                    rightToLeftPortalCache.remove(rightPrimaryKey);
123    
124                    Class<R> rightModelClass = rightBasePersistence.getModelClass();
125    
126                    ModelListener<L>[] leftModelListeners =
127                            leftBasePersistence.getListeners();
128    
129                    for (ModelListener<L> leftModelListener : leftModelListeners) {
130                            leftModelListener.onBeforeAddAssociation(
131                                    leftPrimaryKey, rightModelClass.getName(), rightPrimaryKey);
132                    }
133    
134                    Class<L> leftModelClass = leftBasePersistence.getModelClass();
135    
136                    ModelListener<R>[] rightModelListeners =
137                            rightBasePersistence.getListeners();
138    
139                    for (ModelListener<R> rightModelListener : rightModelListeners) {
140                            rightModelListener.onBeforeAddAssociation(
141                                    rightPrimaryKey, leftModelClass.getName(), leftPrimaryKey);
142                    }
143    
144                    try {
145                            addTableMappingSqlUpdate.update(
146                                    companyId, leftPrimaryKey, rightPrimaryKey);
147                    }
148                    catch (Exception e) {
149                            throw new SystemException(e);
150                    }
151    
152                    for (ModelListener<L> leftModelListener : leftModelListeners) {
153                            leftModelListener.onAfterAddAssociation(
154                                    leftPrimaryKey, rightModelClass.getName(), rightPrimaryKey);
155                    }
156    
157                    for (ModelListener<R> rightModelListener : rightModelListeners) {
158                            rightModelListener.onAfterAddAssociation(
159                                    rightPrimaryKey, leftModelClass.getName(), leftPrimaryKey);
160                    }
161    
162                    return true;
163            }
164    
165            @Override
166            public boolean containsTableMapping(
167                    long companyId, long leftPrimaryKey, long rightPrimaryKey) {
168    
169                    return containsTableMapping(
170                            companyId, leftPrimaryKey, rightPrimaryKey, true);
171            }
172    
173            @Override
174            public int deleteLeftPrimaryKeyTableMappings(
175                    long companyId, long leftPrimaryKey) {
176    
177                    return deleteTableMappings(
178                            leftBasePersistence, rightBasePersistence,
179                            getLeftToRightPortalCache(companyId),
180                            getRightToLeftPortalCache(companyId), getRightPrimaryKeysSqlQuery,
181                            deleteLeftPrimaryKeyTableMappingsSqlUpdate, companyId,
182                            leftPrimaryKey);
183            }
184    
185            @Override
186            public int deleteRightPrimaryKeyTableMappings(
187                    long companyId, long rightPrimaryKey) {
188    
189                    return deleteTableMappings(
190                            rightBasePersistence, leftBasePersistence,
191                            getRightToLeftPortalCache(companyId),
192                            getLeftToRightPortalCache(companyId), getLeftPrimaryKeysSqlQuery,
193                            deleteRightPrimaryKeyTableMappingsSqlUpdate, companyId,
194                            rightPrimaryKey);
195            }
196    
197            @Override
198            public boolean deleteTableMapping(
199                    long companyId, long leftPrimaryKey, long rightPrimaryKey) {
200    
201                    if (!containsTableMapping(
202                                    companyId, leftPrimaryKey, rightPrimaryKey, false)) {
203    
204                            return false;
205                    }
206    
207                    PortalCache<Long, long[]> leftToRightPortalCache =
208                            getLeftToRightPortalCache(companyId);
209    
210                    leftToRightPortalCache.remove(leftPrimaryKey);
211    
212                    PortalCache<Long, long[]> rightToLeftPortalCache =
213                            getRightToLeftPortalCache(companyId);
214    
215                    rightToLeftPortalCache.remove(rightPrimaryKey);
216    
217                    Class<R> rightModelClass = rightBasePersistence.getModelClass();
218    
219                    ModelListener<L>[] leftModelListeners =
220                            leftBasePersistence.getListeners();
221    
222                    for (ModelListener<L> leftModelListener : leftModelListeners) {
223                            leftModelListener.onBeforeRemoveAssociation(
224                                    leftPrimaryKey, rightModelClass.getName(), rightPrimaryKey);
225                    }
226    
227                    Class<L> leftModelClass = leftBasePersistence.getModelClass();
228    
229                    ModelListener<R>[] rightModelListeners =
230                            rightBasePersistence.getListeners();
231    
232                    for (ModelListener<R> rightModelListener : rightModelListeners) {
233                            rightModelListener.onBeforeRemoveAssociation(
234                                    rightPrimaryKey, leftModelClass.getName(), leftPrimaryKey);
235                    }
236    
237                    int rowCount = 0;
238    
239                    try {
240                            rowCount = deleteTableMappingSqlUpdate.update(
241                                    companyId, leftPrimaryKey, rightPrimaryKey);
242                    }
243                    catch (Exception e) {
244                            throw new SystemException(e);
245                    }
246    
247                    if (rowCount > 0) {
248                            for (ModelListener<L> leftModelListener : leftModelListeners) {
249                                    leftModelListener.onAfterRemoveAssociation(
250                                            leftPrimaryKey, rightModelClass.getName(), rightPrimaryKey);
251                            }
252    
253                            for (ModelListener<R> rightModelListener : rightModelListeners) {
254                                    rightModelListener.onAfterRemoveAssociation(
255                                            rightPrimaryKey, leftModelClass.getName(), leftPrimaryKey);
256                            }
257    
258                            return true;
259                    }
260    
261                    return false;
262            }
263    
264            @Override
265            public void destroy() {
266                    Set<Entry<Long, String>> set = _leftToRightPortalCacheNames.entrySet();
267    
268                    Iterator<Entry<Long, String>> iterator = set.iterator();
269    
270                    while (iterator.hasNext()) {
271                            Entry<Long, String> entry = iterator.next();
272    
273                            MultiVMPoolUtil.removePortalCache(entry.getValue());
274    
275                            iterator.remove();
276                    }
277    
278                    set = _rightToLeftPortalCacheNames.entrySet();
279    
280                    iterator = set.iterator();
281    
282                    while (iterator.hasNext()) {
283                            Entry<Long, String> entry = iterator.next();
284    
285                            MultiVMPoolUtil.removePortalCache(entry.getValue());
286    
287                            iterator.remove();
288                    }
289            }
290    
291            @Override
292            public List<L> getLeftBaseModels(
293                    long companyId, long rightPrimaryKey, int start, int end,
294                    OrderByComparator<L> obc) {
295    
296                    return getBaseModels(
297                            getRightToLeftPortalCache(companyId), getLeftPrimaryKeysSqlQuery,
298                            companyId, rightPrimaryKey, leftBasePersistence, start, end, obc);
299            }
300    
301            @Override
302            public long[] getLeftPrimaryKeys(long companyId, long rightPrimaryKey) {
303                    return getPrimaryKeys(
304                            getRightToLeftPortalCache(companyId), getLeftPrimaryKeysSqlQuery,
305                            companyId, rightPrimaryKey, true);
306            }
307    
308            @Override
309            public TableMapper<R, L> getReverseTableMapper() {
310                    return reverseTableMapper;
311            }
312    
313            @Override
314            public List<R> getRightBaseModels(
315                    long companyId, long leftPrimaryKey, int start, int end,
316                    OrderByComparator<R> obc) {
317    
318                    return getBaseModels(
319                            getLeftToRightPortalCache(companyId), getRightPrimaryKeysSqlQuery,
320                            companyId, leftPrimaryKey, rightBasePersistence, start, end, obc);
321            }
322    
323            @Override
324            public long[] getRightPrimaryKeys(long companyId, long leftPrimaryKey) {
325                    return getPrimaryKeys(
326                            getLeftToRightPortalCache(companyId), getRightPrimaryKeysSqlQuery,
327                            companyId, leftPrimaryKey, true);
328            }
329    
330            @Override
331            public boolean matches(String leftColumnName, String rightColumnName) {
332                    if (this.leftColumnName.equals(leftColumnName) &&
333                            this.rightColumnName.equals(rightColumnName)) {
334    
335                            return true;
336                    }
337    
338                    return false;
339            }
340    
341            public void setReverseTableMapper(TableMapper<R, L> reverseTableMapper) {
342                    this.reverseTableMapper = reverseTableMapper;
343            }
344    
345            protected static <M extends BaseModel<M>, S extends BaseModel<S>> int
346                    deleteTableMappings(
347                            BasePersistence<M> masterBasePersistence,
348                            BasePersistence<S> slaveBasePersistence,
349                            PortalCache<Long, long[]> masterToSlavePortalCache,
350                            PortalCache<Long, long[]> slaveToMasterPortalCache,
351                            MappingSqlQuery<Long> mappingSqlQuery, SqlUpdate deleteSqlUpdate,
352                            long companyId, long masterPrimaryKey) {
353    
354                    ModelListener<M>[] masterModelListeners =
355                            masterBasePersistence.getListeners();
356                    ModelListener<S>[] slaveModelListeners =
357                            slaveBasePersistence.getListeners();
358    
359                    long[] slavePrimaryKeys = getPrimaryKeys(
360                            masterToSlavePortalCache, mappingSqlQuery, companyId,
361                            masterPrimaryKey, false);
362    
363                    Class<M> masterModelClass = null;
364                    Class<S> slaveModelClass = null;
365    
366                    if ((masterModelListeners.length > 0) ||
367                            (slaveModelListeners.length > 0)) {
368    
369                            masterModelClass = masterBasePersistence.getModelClass();
370                            slaveModelClass = slaveBasePersistence.getModelClass();
371    
372                            for (long slavePrimaryKey : slavePrimaryKeys) {
373                                    for (ModelListener<M> masterModelListener :
374                                                    masterModelListeners) {
375    
376                                            masterModelListener.onBeforeRemoveAssociation(
377                                                    masterPrimaryKey, slaveModelClass.getName(),
378                                                    slavePrimaryKey);
379                                    }
380    
381                                    for (ModelListener<S> slaveModelListener :
382                                                    slaveModelListeners) {
383    
384                                            slaveModelListener.onBeforeRemoveAssociation(
385                                                    slavePrimaryKey, masterModelClass.getName(),
386                                                    masterPrimaryKey);
387                                    }
388                            }
389                    }
390    
391                    masterToSlavePortalCache.remove(masterPrimaryKey);
392    
393                    for (long slavePrimaryKey : slavePrimaryKeys) {
394                            slaveToMasterPortalCache.remove(slavePrimaryKey);
395                    }
396    
397                    int rowCount = 0;
398    
399                    try {
400                            rowCount = deleteSqlUpdate.update(companyId, masterPrimaryKey);
401                    }
402                    catch (Exception e) {
403                            throw new SystemException(e);
404                    }
405    
406                    if ((masterModelListeners.length > 0) ||
407                            (slaveModelListeners.length > 0)) {
408    
409                            for (long slavePrimaryKey : slavePrimaryKeys) {
410                                    for (ModelListener<M> masterModelListener :
411                                                    masterModelListeners) {
412    
413                                            masterModelListener.onAfterRemoveAssociation(
414                                                    masterPrimaryKey, slaveModelClass.getName(),
415                                                    slavePrimaryKey);
416                                    }
417    
418                                    for (ModelListener<S> slaveModelListener :
419                                                    slaveModelListeners) {
420    
421                                            slaveModelListener.onAfterRemoveAssociation(
422                                                    slavePrimaryKey, masterModelClass.getName(),
423                                                    masterPrimaryKey);
424                                    }
425                            }
426                    }
427    
428                    return rowCount;
429            }
430    
431            protected static <T extends BaseModel<T>> List<T>
432                    getBaseModels(
433                            PortalCache<Long, long[]> portalCache,
434                            MappingSqlQuery<Long> mappingSqlQuery, long companyId,
435                            long masterPrimaryKey, BasePersistence<T> slaveBasePersistence,
436                            int start, int end, OrderByComparator<T> obc) {
437    
438                    long[] slavePrimaryKeys = getPrimaryKeys(
439                            portalCache, mappingSqlQuery, companyId, masterPrimaryKey, true);
440    
441                    if (slavePrimaryKeys.length == 0) {
442                            return Collections.emptyList();
443                    }
444    
445                    List<T> slaveBaseModels = new ArrayList<>(slavePrimaryKeys.length);
446    
447                    try {
448                            for (long slavePrimaryKey : slavePrimaryKeys) {
449                                    slaveBaseModels.add(
450                                            slaveBasePersistence.findByPrimaryKey(slavePrimaryKey));
451                            }
452                    }
453                    catch (NoSuchModelException nsme) {
454                            throw new SystemException(nsme);
455                    }
456    
457                    if (obc != null) {
458                            Collections.sort(slaveBaseModels, obc);
459                    }
460    
461                    return ListUtil.subList(slaveBaseModels, start, end);
462            }
463    
464            protected static long[] getPrimaryKeys(
465                    PortalCache<Long, long[]> portalCache,
466                    MappingSqlQuery<Long> mappingSqlQuery, long companyId,
467                    long masterPrimaryKey, boolean updateCache) {
468    
469                    long[] primaryKeys = portalCache.get(masterPrimaryKey);
470    
471                    if (primaryKeys == null) {
472                            List<Long> primaryKeysList = null;
473    
474                            try {
475                                    primaryKeysList = mappingSqlQuery.execute(
476                                            companyId, masterPrimaryKey);
477                            }
478                            catch (Exception e) {
479                                    throw new SystemException(e);
480                            }
481    
482                            primaryKeys = new long[primaryKeysList.size()];
483    
484                            for (int i = 0; i < primaryKeys.length; i++) {
485                                    primaryKeys[i] = primaryKeysList.get(i);
486                            }
487    
488                            Arrays.sort(primaryKeys);
489    
490                            if (updateCache) {
491                                    PortalCacheHelperUtil.putWithoutReplicator(
492                                            portalCache, masterPrimaryKey, primaryKeys);
493                            }
494                    }
495    
496                    return primaryKeys;
497            }
498    
499            protected boolean containsTableMapping(
500                    long companyId, long leftPrimaryKey, long rightPrimaryKey,
501                    boolean updateCache) {
502    
503                    long[] rightPrimaryKeys = getPrimaryKeys(
504                            getLeftToRightPortalCache(companyId), getRightPrimaryKeysSqlQuery,
505                            companyId, leftPrimaryKey, updateCache);
506    
507                    if (Arrays.binarySearch(rightPrimaryKeys, rightPrimaryKey) < 0) {
508                            return false;
509                    }
510                    else {
511                            return true;
512                    }
513            }
514    
515            protected PortalCache<Long, long[]> getLeftToRightPortalCache(
516                    long companyId) {
517    
518                    String portalCacheName = _leftToRightPortalCacheNames.get(companyId);
519    
520                    if (portalCacheName == null) {
521                            portalCacheName =
522                                    TableMapper.class.getName() + "-" + tableName +
523                                            "-LeftToRight-" + companyId;
524    
525                            _leftToRightPortalCacheNames.put(companyId, portalCacheName);
526                    }
527    
528                    return MultiVMPoolUtil.getPortalCache(portalCacheName);
529            }
530    
531            protected PortalCache<Long, long[]> getRightToLeftPortalCache(
532                    long companyId) {
533    
534                    String portalCacheName = _rightToLeftPortalCacheNames.get(companyId);
535    
536                    if (portalCacheName == null) {
537                            portalCacheName =
538                                    TableMapper.class.getName() + "-" + tableName +
539                                            "-RightToLeft-" + companyId;
540    
541                            _rightToLeftPortalCacheNames.put(companyId, portalCacheName);
542                    }
543    
544                    return MultiVMPoolUtil.getPortalCache(portalCacheName);
545            }
546    
547            protected SqlUpdate addTableMappingSqlUpdate;
548            protected String companyColumnName;
549            protected SqlUpdate deleteLeftPrimaryKeyTableMappingsSqlUpdate;
550            protected SqlUpdate deleteRightPrimaryKeyTableMappingsSqlUpdate;
551            protected SqlUpdate deleteTableMappingSqlUpdate;
552            protected MappingSqlQuery<Long> getLeftPrimaryKeysSqlQuery;
553            protected MappingSqlQuery<Long> getRightPrimaryKeysSqlQuery;
554            protected BasePersistence<L> leftBasePersistence;
555            protected String leftColumnName;
556            protected TableMapper<R, L> reverseTableMapper;
557            protected BasePersistence<R> rightBasePersistence;
558            protected String rightColumnName;
559            protected String tableName;
560    
561            private final Map<Long, String> _leftToRightPortalCacheNames =
562                    new ConcurrentHashMap<>();
563            private final Map<Long, String> _rightToLeftPortalCacheNames =
564                    new ConcurrentHashMap<>();
565    
566    }