Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.domain.propval.ibatis; import java.io.Serializable; import java.util.Date; import java.util.List; import org.alfresco.ibatis.RollupResultHandler; import org.alfresco.repo.domain.propval.AbstractPropertyValueDAOImpl; import org.alfresco.repo.domain.propval.PropertyClassEntity; import org.alfresco.repo.domain.propval.PropertyDateValueEntity; import org.alfresco.repo.domain.propval.PropertyDoubleValueEntity; import org.alfresco.repo.domain.propval.PropertyIdQueryParameter; import org.alfresco.repo.domain.propval.PropertyIdQueryResult; import org.alfresco.repo.domain.propval.PropertyIdSearchRow; import org.alfresco.repo.domain.propval.PropertyLinkEntity; import org.alfresco.repo.domain.propval.PropertyRootEntity; import org.alfresco.repo.domain.propval.PropertySerializableValueEntity; import org.alfresco.repo.domain.propval.PropertyStringQueryEntity; import org.alfresco.repo.domain.propval.PropertyStringValueEntity; import org.alfresco.repo.domain.propval.PropertyUniqueContextEntity; import org.alfresco.repo.domain.propval.PropertyValueEntity; import org.alfresco.repo.domain.propval.PropertyValueEntity.PersistedType; import org.alfresco.repo.domain.schema.script.ScriptBundleExecutor; import org.alfresco.util.Pair; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.DuplicateKeyException; /** * iBatis-specific implementation of the PropertyValue DAO. * * @author Derek Hulley * @since 3.2 */ public class PropertyValueDAOImpl extends AbstractPropertyValueDAOImpl { private static final String SELECT_PROPERTY_CLASS_BY_ID = "alfresco.propval.select_PropertyClassByID"; private static final String SELECT_PROPERTY_CLASS_BY_NAME = "alfresco.propval.select_PropertyClassByName"; private static final String INSERT_PROPERTY_CLASS = "alfresco.propval.insert.insert_PropertyClass"; private static final String SELECT_PROPERTY_DATE_VALUE_BY_ID = "alfresco.propval.select_PropertyDateValueByID"; private static final String SELECT_PROPERTY_DATE_VALUE_BY_VALUE = "alfresco.propval.select_PropertyDateValueByValue"; private static final String INSERT_PROPERTY_DATE_VALUE = "alfresco.propval.insert_PropertyDateValue"; private static final String SELECT_PROPERTY_STRING_VALUE_BY_ID = "alfresco.propval.select_PropertyStringValueByID"; private static final String SELECT_PROPERTY_STRING_VALUE_BY_VALUE = "alfresco.propval.select_PropertyStringValueByValue"; private static final String INSERT_PROPERTY_STRING_VALUE = "alfresco.propval.insert.insert_PropertyStringValue"; private static final String SELECT_PROPERTY_DOUBLE_VALUE_BY_ID = "alfresco.propval.select_PropertyDoubleValueByID"; private static final String SELECT_PROPERTY_DOUBLE_VALUE_BY_VALUE = "alfresco.propval.select_PropertyDoubleValueByValue"; private static final String INSERT_PROPERTY_DOUBLE_VALUE = "alfresco.propval.insert.insert_PropertyDoubleValue"; private static final String SELECT_PROPERTY_SERIALIZABLE_VALUE_BY_ID = "alfresco.propval.select_PropertySerializableValueByID"; private static final String INSERT_PROPERTY_SERIALIZABLE_VALUE = "alfresco.propval.insert.insert_PropertySerializableValue"; private static final String SELECT_PROPERTY_VALUE_BY_ID = "alfresco.propval.select_PropertyValueById"; private static final String SELECT_PROPERTY_VALUE_BY_LOCAL_VALUE = "alfresco.propval.select_PropertyValueByLocalValue"; private static final String SELECT_PROPERTY_VALUE_BY_DOUBLE_VALUE = "alfresco.propval.select_PropertyValueByDoubleValue"; private static final String SELECT_PROPERTY_VALUE_BY_STRING_VALUE = "alfresco.propval.select_PropertyValueByStringValue"; private static final String INSERT_PROPERTY_VALUE = "alfresco.propval.insert.insert_PropertyValue"; private static final String SELECT_PROPERTY_BY_ID = "alfresco.propval.select_PropertyById"; private static final String SELECT_PROPERTIES_BY_IDS = "alfresco.propval.select_PropertiesByIds"; private static final String SELECT_PROPERTY_ROOT_BY_ID = "alfresco.propval.select_PropertyRootById"; private static final String INSERT_PROPERTY_ROOT = "alfresco.propval.insert.insert_PropertyRoot"; private static final String UPDATE_PROPERTY_ROOT = "alfresco.propval.update_PropertyRoot"; private static final String DELETE_PROPERTY_ROOT_BY_ID = "alfresco.propval.delete_PropertyRootById"; private static final String SELECT_PROPERTY_UNIQUE_CTX_BY_ID = "alfresco.propval.select_PropertyUniqueContextById"; private static final String SELECT_PROPERTY_UNIQUE_CTX_BY_VALUES = "alfresco.propval.select_PropertyUniqueContextByValues"; private static final String INSERT_PROPERTY_UNIQUE_CTX = "alfresco.propval.insert.insert_PropertyUniqueContext"; private static final String UPDATE_PROPERTY_UNIQUE_CTX = "alfresco.propval.update_PropertyUniqueContext"; private static final String DELETE_PROPERTY_UNIQUE_CTX_BY_ID = "alfresco.propval.delete_PropertyUniqueContextById"; private static final String DELETE_PROPERTY_UNIQUE_CTX_BY_VALUES = "alfresco.propval.delete_PropertyUniqueContextByValues"; private static final String INSERT_PROPERTY_LINK = "alfresco.propval.insert_PropertyLink"; private static final String DELETE_PROPERTY_LINKS_BY_ROOT_ID = "alfresco.propval.delete_PropertyLinksByRootId"; private SqlSessionTemplate template; private ScriptBundleExecutor scriptExecutor; public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.template = sqlSessionTemplate; } public void setScriptExecutor(ScriptBundleExecutor scriptExecutor) { this.scriptExecutor = scriptExecutor; } //================================ // 'alf_prop_class' accessors //================================ @Override protected PropertyClassEntity findClassById(Long id) { PropertyClassEntity entity = new PropertyClassEntity(); entity.setId(id); entity = template.selectOne(SELECT_PROPERTY_CLASS_BY_ID, entity); // Done return entity; } @Override protected PropertyClassEntity findClassByValue(Class<?> value) { PropertyClassEntity entity = new PropertyClassEntity(); entity.setJavaClass(value); entity = template.selectOne(SELECT_PROPERTY_CLASS_BY_NAME, entity); // Done return entity; } @Override protected PropertyClassEntity createClass(Class<?> value) { PropertyClassEntity entity = new PropertyClassEntity(); entity.setJavaClass(value); template.insert(INSERT_PROPERTY_CLASS, entity); // Done return entity; } //================================ // 'alf_prop_date_value' accessors //================================ @Override protected PropertyDateValueEntity findDateValueById(Long id) { PropertyDateValueEntity entity = template.selectOne(SELECT_PROPERTY_DATE_VALUE_BY_ID, id); // Done return entity; } @Override protected PropertyDateValueEntity findDateValueByValue(Date value) { PropertyDateValueEntity result = template.selectOne(SELECT_PROPERTY_DATE_VALUE_BY_VALUE, new Long(value.getTime())); // The ID is the actual time in ms (GMT) return result; } @Override protected PropertyDateValueEntity createDateValue(Date value) { PropertyDateValueEntity entity = new PropertyDateValueEntity(); entity.setValue(value); template.insert(INSERT_PROPERTY_DATE_VALUE, entity); // Done return entity; } //================================ // 'alf_prop_string_value' accessors //================================ @Override protected String findStringValueById(Long id) { PropertyStringValueEntity entity = new PropertyStringValueEntity(); entity.setId(id); String value = template.selectOne(SELECT_PROPERTY_STRING_VALUE_BY_ID, entity); // Done return value; } @Override protected Long findStringValueByValue(String value) { PropertyStringValueEntity entity = new PropertyStringValueEntity(); entity.setValue(value); List<Long> rows = template.selectList(SELECT_PROPERTY_STRING_VALUE_BY_VALUE, entity, new RowBounds(0, 1)); // The CRC match prevents incorrect results from coming back. Although there could be // several matches, we are sure that the matches are case-sensitive. if (rows.size() > 0) { return rows.get(0); } else { return null; } } @Override protected Long createStringValue(String value) { PropertyStringValueEntity entity = new PropertyStringValueEntity(); entity.setValue(value); template.insert(INSERT_PROPERTY_STRING_VALUE, entity); // Done return entity.getId(); } //================================ // 'alf_prop_double_value' accessors //================================ @Override protected PropertyDoubleValueEntity findDoubleValueById(Long id) { PropertyDoubleValueEntity entity = new PropertyDoubleValueEntity(); entity.setId(id); entity = template.selectOne(SELECT_PROPERTY_DOUBLE_VALUE_BY_ID, entity); // Done return entity; } @Override protected PropertyDoubleValueEntity findDoubleValueByValue(Double value) { PropertyDoubleValueEntity entity = new PropertyDoubleValueEntity(); entity.setDoubleValue(value); List<PropertyDoubleValueEntity> results = template.selectList(SELECT_PROPERTY_DOUBLE_VALUE_BY_VALUE, entity, new RowBounds(0, 1)); // There could be several matches, so just get one if (results.size() > 0) { return results.get(0); } else { // No match return null; } } @Override protected PropertyDoubleValueEntity createDoubleValue(Double value) { PropertyDoubleValueEntity entity = new PropertyDoubleValueEntity(); entity.setDoubleValue(value); template.insert(INSERT_PROPERTY_DOUBLE_VALUE, entity); // Done return entity; } //================================ // 'alf_prop_serializable_value' accessors //================================ @Override protected PropertySerializableValueEntity findSerializableValueById(Long id) { PropertySerializableValueEntity entity = new PropertySerializableValueEntity(); entity.setId(id); entity = template.selectOne(SELECT_PROPERTY_SERIALIZABLE_VALUE_BY_ID, entity); // Done return entity; } @Override protected PropertySerializableValueEntity createSerializableValue(Serializable value) { PropertySerializableValueEntity entity = new PropertySerializableValueEntity(); entity.setSerializableValue(value); template.insert(INSERT_PROPERTY_SERIALIZABLE_VALUE, entity); // Done return entity; } //================================ // 'alf_prop_value' accessors //================================ @Override protected PropertyValueEntity findPropertyValueById(Long id) { PropertyValueEntity entity = new PropertyValueEntity(); entity.setId(id); List<PropertyValueEntity> results = template.selectList(SELECT_PROPERTY_VALUE_BY_ID, entity); // At most one of the results represents a real value int size = results.size(); if (size == 0) { return null; } else if (size == 1) { return results.get(0); } else { logger.error("Found property value linked to multiple raw types: " + results); return results.get(0); } } @Override protected PropertyValueEntity findPropertyValueByValue(Serializable value) { // Get the actual type ID Class<?> clazz = (value == null ? Object.class : value.getClass()); Pair<Long, Class<?>> clazzPair = getPropertyClass(clazz); if (clazzPair == null) { // Shortcut: There are no properties of this type return null; } Long actualTypeId = clazzPair.getFirst(); // Construct the search parameters PropertyValueEntity queryEntity = new PropertyValueEntity(); queryEntity.setValue(value, converter); queryEntity.setActualTypeId(actualTypeId); // How would it be persisted? PersistedType persistedType = queryEntity.getPersistedTypeEnum(); Short persistedTypeId = queryEntity.getPersistedType(); // Query based on the the persistable value type String query = null; Object queryObject = queryEntity; // Handle each persisted type individually switch (persistedType) { case NULL: case LONG: query = SELECT_PROPERTY_VALUE_BY_LOCAL_VALUE; break; case DOUBLE: query = SELECT_PROPERTY_VALUE_BY_DOUBLE_VALUE; break; case CONSTRUCTABLE: // The string value is the name of the class (e.g. 'java.util.HashMap') case ENUM: // The string-equivalent representation case STRING: // It's best to query using the CRC and short end-value query = SELECT_PROPERTY_VALUE_BY_STRING_VALUE; queryObject = new PropertyStringQueryEntity(persistedTypeId, actualTypeId, queryEntity.getStringValue()); break; case SERIALIZABLE: // No query break; default: throw new IllegalStateException("Unhandled PersistedType value: " + persistedType); } // Now query PropertyValueEntity result = null; if (query != null) { // Uniqueness is guaranteed by the tables, so we get one value only result = template.selectOne(query, queryObject); } // Done return result; } @Override protected PropertyValueEntity createPropertyValue(Serializable value) { try { return createPropertyValueInternal(value); } catch (DuplicateKeyException e) { //In very rare situation, it can fail. Just try one more time. //See MNT-12770 for details return createPropertyValueInternal(value); } } private PropertyValueEntity createPropertyValueInternal(Serializable value) { // Get the actual type ID Class<?> clazz = (value == null ? Object.class : value.getClass()); Pair<Long, Class<?>> clazzPair = getOrCreatePropertyClass(clazz); Long actualTypeId = clazzPair.getFirst(); // Construct the insert entity PropertyValueEntity insertEntity = new PropertyValueEntity(); insertEntity.setValue(value, converter); insertEntity.setActualTypeId(actualTypeId); // Persist the persisted value switch (insertEntity.getPersistedTypeEnum()) { case DOUBLE: Double doubleValue = insertEntity.getDoubleValue(); Pair<Long, Double> insertDoublePair = getOrCreatePropertyDoubleValue(doubleValue); insertEntity.setLongValue(insertDoublePair.getFirst()); break; case STRING: case CONSTRUCTABLE: case ENUM: String stringValue = insertEntity.getStringValue(); Pair<Long, String> insertStringPair = getOrCreatePropertyStringValue(stringValue); insertEntity.setLongValue(insertStringPair.getFirst()); break; case SERIALIZABLE: Pair<Long, Serializable> insertSerializablePair = createPropertySerializableValue(value); insertEntity.setLongValue(insertSerializablePair.getFirst()); break; case NULL: case LONG: // Do nothing for these break; default: throw new IllegalStateException("Unknown PersistedType enum: " + insertEntity.getPersistedTypeEnum()); } // Persist the entity template.insert(INSERT_PROPERTY_VALUE, insertEntity); // Done return insertEntity; } //================================ // 'alf_prop_root' accessors //================================ @Override protected List<PropertyIdSearchRow> findPropertyById(Long id) { PropertyValueEntity entity = new PropertyValueEntity(); entity.setId(id); List<PropertyIdSearchRow> results = template.selectList(SELECT_PROPERTY_BY_ID, entity); return results; } private static final String[] KEY_COLUMNS_FINDBYIDS = new String[] { "propId" }; @Override protected void findPropertiesByIds(List<Long> ids, final PropertyFinderCallback callback) { ResultHandler valueResultHandler = new ResultHandler() { public void handleResult(ResultContext context) { PropertyIdQueryResult result = (PropertyIdQueryResult) context.getResultObject(); Long id = result.getPropId(); // Make the serializable value List<PropertyIdSearchRow> rows = result.getPropValues(); Serializable value = convertPropertyIdSearchRows(rows); callback.handleProperty(id, value); } }; // A row handler to roll up individual rows Configuration configuration = template.getConfiguration(); RollupResultHandler rollupResultHandler = new RollupResultHandler(configuration, KEY_COLUMNS_FINDBYIDS, "propValues", valueResultHandler); // Query using the IDs PropertyIdQueryParameter params = new PropertyIdQueryParameter(); params.setRootPropIds(ids); template.select(SELECT_PROPERTIES_BY_IDS, params, rollupResultHandler); // Process any remaining results rollupResultHandler.processLastResults(); // Done } @Override protected Long createPropertyRoot() { PropertyRootEntity rootEntity = new PropertyRootEntity(); rootEntity.setVersion((short) 0); template.insert(INSERT_PROPERTY_ROOT, rootEntity); return rootEntity.getId(); } @Override protected PropertyRootEntity getPropertyRoot(Long id) { PropertyRootEntity entity = new PropertyRootEntity(); entity.setId(id); return template.selectOne(SELECT_PROPERTY_ROOT_BY_ID, entity); } @Override protected PropertyRootEntity updatePropertyRoot(PropertyRootEntity entity) { entity.incrementVersion(); int updated = template.update(UPDATE_PROPERTY_ROOT, entity); if (updated != 1) { // unexpected number of rows affected throw new ConcurrencyFailureException("Incorrect number of rows affected for updatePropertyRoot: " + entity + ": expected 1, actual " + updated); } return entity; } @Override protected void deletePropertyRoot(Long id) { PropertyRootEntity entity = new PropertyRootEntity(); entity.setId(id); template.delete(DELETE_PROPERTY_ROOT_BY_ID, entity); } @Override protected PropertyUniqueContextEntity createPropertyUniqueContext(Long valueId1, Long valueId2, Long valueId3, Long propertyId) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); entity.setValue1PropId(valueId1); entity.setValue2PropId(valueId2); entity.setValue3PropId(valueId3); entity.setPropertyId(propertyId); template.insert(INSERT_PROPERTY_UNIQUE_CTX, entity); return entity; } @Override protected PropertyUniqueContextEntity getPropertyUniqueContextById(Long id) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); entity.setId(id); entity = template.selectOne(SELECT_PROPERTY_UNIQUE_CTX_BY_ID, entity); return entity; } @Override protected PropertyUniqueContextEntity getPropertyUniqueContextByValues(Long valueId1, Long valueId2, Long valueId3) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); entity.setValue1PropId(valueId1); entity.setValue2PropId(valueId2); entity.setValue3PropId(valueId3); entity = template.selectOne(SELECT_PROPERTY_UNIQUE_CTX_BY_VALUES, entity); return entity; } @Override protected void getPropertyUniqueContextByValues(final PropertyUniqueContextCallback callback, Long... valueIds) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); for (int i = 0; i < valueIds.length; i++) { switch (i) { case 0: entity.setValue1PropId(valueIds[i]); break; case 1: entity.setValue2PropId(valueIds[i]); break; case 2: entity.setValue3PropId(valueIds[i]); break; default: throw new IllegalArgumentException("Only 3 ids allowed"); } } ResultHandler valueResultHandler = new ResultHandler() { public void handleResult(ResultContext context) { PropertyUniqueContextEntity result = (PropertyUniqueContextEntity) context.getResultObject(); Long id = result.getId(); Long propId = result.getPropertyId(); Serializable[] keys = new Serializable[3]; keys[0] = result.getValue1PropId(); keys[1] = result.getValue2PropId(); keys[2] = result.getValue3PropId(); callback.handle(id, propId, keys); } }; template.select(SELECT_PROPERTY_UNIQUE_CTX_BY_VALUES, entity, valueResultHandler); // Done } @Override protected PropertyUniqueContextEntity updatePropertyUniqueContext(PropertyUniqueContextEntity entity) { entity.incrementVersion(); int updated = template.update(UPDATE_PROPERTY_UNIQUE_CTX, entity); if (updated != 1) { // unexpected number of rows affected throw new ConcurrencyFailureException( "Incorrect number of rows affected for updatePropertyUniqueContext: " + entity + ": expected 1, actual " + updated); } return entity; } public void deletePropertyUniqueContext(Long id) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); entity.setId(id); template.delete(DELETE_PROPERTY_UNIQUE_CTX_BY_ID, entity); } @Override protected int deletePropertyUniqueContexts(Long... valueIds) { PropertyUniqueContextEntity entity = new PropertyUniqueContextEntity(); for (int i = 0; i < valueIds.length; i++) { switch (i) { case 0: entity.setValue1PropId(valueIds[i]); break; case 1: entity.setValue2PropId(valueIds[i]); break; case 2: entity.setValue3PropId(valueIds[i]); break; default: throw new IllegalArgumentException("Only 3 ids allowed"); } } return template.delete(DELETE_PROPERTY_UNIQUE_CTX_BY_VALUES, entity); } @Override protected void createPropertyLink(Long rootPropId, Long propIndex, Long containedIn, Long keyPropId, Long valuePropId) { PropertyLinkEntity insertEntity = new PropertyLinkEntity(); insertEntity.setRootPropId(rootPropId); insertEntity.setPropIndex(propIndex); insertEntity.setContainedIn(containedIn); insertEntity.setKeyPropId(keyPropId); insertEntity.setValuePropId(valuePropId); template.insert(INSERT_PROPERTY_LINK, insertEntity); // Done } @Override protected int deletePropertyLinks(Long rootPropId) { PropertyRootEntity entity = new PropertyRootEntity(); entity.setId(rootPropId); return template.delete(DELETE_PROPERTY_LINKS_BY_ROOT_ID, entity); } @Override public void cleanupUnusedValues() { // execute clean up in case of previous failures try { scriptExecutor.exec("alfresco/dbscripts/utility/${db.script.dialect}", "CleanAlfPropTablesPostExec.sql"); } catch (RuntimeException e) { logger.error("The pre-exec cleanup script failed: ", e); } // Run the main script try { scriptExecutor.exec(false, "alfresco/dbscripts/utility/${db.script.dialect}", "CleanAlfPropTables.sql"); } catch (RuntimeException e) { logger.error("The cleanup script failed: ", e); throw e; } finally { try { // execute clean up scriptExecutor.exec(false, "alfresco/dbscripts/utility/${db.script.dialect}", "CleanAlfPropTablesPostExec.sql"); } catch (RuntimeException e) { logger.error("The post-exec cleanup script failed: ", e); // We do not rethrow this as we want to preserve the original failure } clearCaches(); } } }