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