/* * Copyright 2014 - 2017 Blazebit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.blazebit.persistence.view.impl.update; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.Query; import com.blazebit.persistence.view.impl.collection.RecordingCollection; import com.blazebit.persistence.view.impl.collection.RecordingMap; import com.blazebit.persistence.view.impl.proxy.UpdatableProxy; import com.blazebit.persistence.view.impl.tx.TransactionHelper; import com.blazebit.persistence.view.impl.tx.TransactionSynchronizationStrategy; import com.blazebit.persistence.view.impl.update.flush.BasicAttributeFlusher; import com.blazebit.persistence.view.impl.update.flush.CollectionAttributeFlusher; import com.blazebit.persistence.view.impl.update.flush.DirtyAttributeFlusher; import com.blazebit.persistence.view.impl.update.flush.MapAttributeFlusher; import com.blazebit.persistence.view.metamodel.MapAttribute; import com.blazebit.persistence.view.metamodel.MappingAttribute; import com.blazebit.persistence.view.metamodel.MethodAttribute; import com.blazebit.persistence.view.metamodel.ViewType; import com.blazebit.reflection.ExpressionUtils; import com.blazebit.reflection.PropertyPathExpression; public class FullEntityViewUpdater implements EntityViewUpdater { private final Class<?> entityClass; private final String idAttributeName; private final DirtyAttributeFlusher<Object, Object>[] dirtyAttributeFlushers; private final boolean useQueryFlush; private final String updateQuery; @SuppressWarnings({ "unchecked", "rawtypes" }) public FullEntityViewUpdater(ViewType<?> viewType) { this.entityClass = viewType.getEntityClass(); Set<MethodAttribute<?, ?>> attributes = (Set<MethodAttribute<?, ?>>) (Set) viewType.getAttributes(); MethodAttribute<?, ?> idAttribute = viewType.getIdAttribute(); List<DirtyAttributeFlusher<? extends Object, ? extends Object>> flushers = new ArrayList<DirtyAttributeFlusher<? extends Object, ? extends Object>>(attributes.size()); StringBuilder sb = new StringBuilder(100); sb.append("UPDATE " + entityClass.getName() + " SET "); boolean first = true; boolean supportsQueryFlush = true; for (MethodAttribute<?, ?> attribute : attributes) { if (attribute == idAttribute) { continue; } if (attribute.isUpdatable()) { if (first) { first = false; } else { sb.append(", "); } String attributeName = attribute.getName(); String attributeMapping = ((MappingAttribute<?, ?>) viewType.getAttribute(attributeName)).getMapping(); DirtyAttributeFlusher<? extends Object, ? extends Object> flusher; if (attribute.isCollection()) { if (attribute instanceof MapAttribute<?, ?, ?>) { flusher = new MapAttributeFlusher<Object, RecordingMap<Map<?, ?>, ?, ?>>((PropertyPathExpression<Object, Map<?, ?>>) (PropertyPathExpression) ExpressionUtils.getExpression(entityClass, attributeMapping)); } else { flusher = new CollectionAttributeFlusher<Object, RecordingCollection<Collection<?>, ?>>((PropertyPathExpression<Object, Collection<?>>) (PropertyPathExpression) ExpressionUtils.getExpression(entityClass, attributeMapping)); } } else { flusher = new BasicAttributeFlusher<Object, Object>(attributeName, (PropertyPathExpression<Object, Object>) ExpressionUtils.getExpression(entityClass, attributeMapping)); } supportsQueryFlush = supportsQueryFlush && flusher.supportsQueryFlush(); flushers.add(flusher); sb.append(attributeMapping); sb.append(" = :"); sb.append(attributeName); } } dirtyAttributeFlushers = flushers.toArray(new DirtyAttributeFlusher[flushers.size()]); String idName = idAttribute.getName(); sb.append(" WHERE ").append(((MappingAttribute<?, ?>) viewType.getAttribute(idName)).getMapping()).append(" = :").append(idName); idAttributeName = idName; useQueryFlush = supportsQueryFlush; updateQuery = sb.toString(); } @Override public void executeUpdate(EntityManager em, UpdatableProxy updatableProxy) { TransactionSynchronizationStrategy synchronizationStrategy = TransactionHelper.getSynchronizationStrategy(em); if (!synchronizationStrategy.isActive()) { throw new IllegalStateException("Transaction is not active!"); } Object id = updatableProxy.$$_getId(); Object[] dirtyState = updatableProxy.$$_getDirtyState(); if (useQueryFlush) { Query query = em.createQuery(updateQuery); query.setParameter(idAttributeName, id); for (int i = 0; i < dirtyState.length; i++) { dirtyAttributeFlushers[i].flushQuery(query, dirtyState[i]); } int updatedCount = query.executeUpdate(); if (updatedCount != 1) { throw new RuntimeException("Update did not work! Expected to update 1 row but was: " + updatedCount); } } else { Object entity = em.getReference(entityClass, id); for (int i = 0; i < dirtyState.length; i++) { dirtyAttributeFlushers[i].flushEntity(entity, dirtyState[i]); } } } }