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