All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebeaninternal.server.persist.dml.UpdateMeta Maven / Gradle / Ivy

There is a newer version: 2.8.1
Show newest version
/**
 * Copyright (C) 2006  Robin Bygrave
 * 
 * This file is part of Ebean.
 * 
 * Ebean 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 2.1 of the License, or
 * (at your option) any later version.
 *  
 * Ebean 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 Ebean; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA  
 */
package com.avaje.ebeaninternal.server.persist.dml;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.persistence.PersistenceException;

import com.avaje.ebeaninternal.api.SpiUpdatePlan;
import com.avaje.ebeaninternal.server.core.ConcurrencyMode;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList;

/**
 * Meta data for update handler. The meta data is for a particular bean type. It
 * is considered immutable and is thread safe.
 */
public final class UpdateMeta {

	
	private final String sqlVersion;
	
	private final String sqlNone;
	
	private final Bindable set;
	private final Bindable id;
	private final Bindable version;
	private final Bindable all;
	
	private final String tableName;
	
	private final UpdatePlan modeNoneUpdatePlan;
	private final UpdatePlan modeVersionUpdatePlan;
	
	private final boolean emptyStringAsNull;
	
	public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor desc, Bindable set, Bindable id, Bindable version, Bindable all) {
		this.emptyStringAsNull = emptyStringAsNull;
	    this.tableName = desc.getBaseTable();
		this.set = set;
		this.id = id;
		this.version = version;
		this.all = all;
		
		this.sqlNone = genSql(ConcurrencyMode.NONE, null, null);
		this.sqlVersion = genSql(ConcurrencyMode.VERSION, null, null);
		
		this.modeNoneUpdatePlan = new UpdatePlan(ConcurrencyMode.NONE, sqlNone, set);
		this.modeVersionUpdatePlan = new UpdatePlan(ConcurrencyMode.VERSION, sqlVersion, set);

	}
		
	/**
	 * Return true if empty strings should be treated as null.
	 */
	public boolean isEmptyStringAsNull() {
        return emptyStringAsNull;
    }

    /**
	 * Return the base table name.
	 */
	public String getTableName() {
		return tableName;
	}

	/**
	 * Bind the request based on the concurrency mode.
	 */
	public void bind(PersistRequestBean persist, DmlHandler bind, SpiUpdatePlan updatePlan) throws SQLException {

		Object bean = persist.getBean();
		
		bind.bindLogAppend(" set[");
		bind.setCheckDelta(true);
		updatePlan.bindSet(bind, bean);
        bind.setCheckDelta(false);

		bind.bindLogAppend("] where[");
		id.dmlBind(bind, false, bean);

		switch (persist.getConcurrencyMode()) {
		case VERSION:
			version.dmlBind(bind, false, bean);			
			break;
		case ALL:
			Object oldBean = persist.getOldValues();
			all.dmlBindWhere(bind, true, oldBean);
			break;

		default:
			break;
		}
	}
	
	/**
	 * get or generate the sql based on the concurrency mode.
	 */
	public SpiUpdatePlan getUpdatePlan(PersistRequestBean request) {

		ConcurrencyMode mode = request.determineConcurrencyMode();
		if (request.isDynamicUpdateSql()){
			return getDynamicUpdatePlan(mode, request);
		}
		
		// 'full bean' update...
		switch (mode) {
		case NONE:
			return modeNoneUpdatePlan;
			
		case VERSION:
			return modeVersionUpdatePlan;
			
		case ALL:
			Object oldValues = request.getOldValues();
			if (oldValues == null) {
				throw new PersistenceException("OldValues are null?");
			}
			String sql = genDynamicWhere(request.getUpdatedProperties(), request.getLoadedProperties(), oldValues);
			return new UpdatePlan(ConcurrencyMode.ALL, sql, set);

		default:
			throw new RuntimeException("Invalid mode "+mode);
		}
	}

	private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean persistRequest) {

		Set updatedProps = persistRequest.getUpdatedProperties();
		
		if (ConcurrencyMode.ALL.equals(mode)){
			// due to is null in where clause we won't bother trying to 
			// cache plans for ConcurrencyMode.ALL
			String sql = genSql(mode, persistRequest, null);
			if (sql == null){
			    // changed properties must have been updatable=false
			    return UpdatePlan.EMPTY_SET_CLAUSE;
			} else {
			    return new UpdatePlan(null, mode, sql, set, updatedProps);
			}
		}
		
		// we can use a cached UpdatePlan for the changed properties
		int hash =  mode.hashCode();
		hash = hash * 31 + (updatedProps == null ? 0 : updatedProps.hashCode());
		Integer key = Integer.valueOf(hash);
		
		BeanDescriptor beanDescriptor = persistRequest.getBeanDescriptor();
		SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key);
		if (updatePlan != null){
			return updatePlan;
		}
		
		// build a new UpdatePlan and cache it
		
		// build a bindableList that only contains the changed properties
		List list = new ArrayList();
		set.addChanged(persistRequest, list);
		BindableList bindableList = new BindableList(list);
		
		// build the SQL for this update statement
		String sql = genSql(mode, persistRequest, bindableList);
		
		updatePlan = new UpdatePlan(key, mode, sql, bindableList, null);
		
		// add the UpdatePlan to the cache
		beanDescriptor.putUpdatePlan(key, updatePlan);
		
		return updatePlan;
	}
	
	private String genSql(ConcurrencyMode conMode, PersistRequestBean persistRequest, BindableList bindableList) {

		// update  set col0=?, col1=?, col2=? where bcol=? and bc1=? and bc2=?

		GenerateDmlRequest request;
		if (persistRequest == null){
			// For generation of None and Version DML/SQL
			request = new GenerateDmlRequest(emptyStringAsNull);
		} else {
			request = persistRequest.createGenerateDmlRequest(emptyStringAsNull);
		}
				
		request.append("update ").append(tableName).append(" set ");
		
		request.setUpdateSetMode();
		if (bindableList != null){
			bindableList.dmlAppend(request, false);
		} else {
			set.dmlAppend(request, true);
		}
		
		if (request.getBindColumnCount() == 0){
		    // update properties must have been updatable=false
		    // with the result that nothing is in the set clause 
		    return null;
		}
		
		request.append(" where ");
		
		request.setWhereIdMode();
		id.dmlAppend(request, false);
		
		if (ConcurrencyMode.VERSION.equals(conMode)) {
			if (version == null){
				return null;
			}
			version.dmlAppend(request, false);
			
		} else if (ConcurrencyMode.ALL.equals(conMode)) {
			
			all.dmlWhere(request, true, request.getOldValues());
		}
		
		return request.toString();
	}
	
	
	/**
	 * Generate the sql dynamically for where using IS NULL for binding null values.
	 */
	private String genDynamicWhere(Set loadedProps, Set whereProps, Object oldBean) {

		// always has a preceding id property(s) so the first
		// option is always ' and ' and not blank.
		
		GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull, loadedProps, whereProps, oldBean);
		
		request.append(sqlNone);
		
		request.setWhereMode();
		all.dmlWhere(request, true, oldBean);
		
		return request.toString();
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy