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