001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc;
023:
024: import java.sql.Connection;
025: import java.sql.PreparedStatement;
026: import javax.ejb.EJBException;
027:
028: import org.jboss.ejb.EntityEnterpriseContext;
029: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
030: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
031: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
032: import org.jboss.logging.Logger;
033:
034: /**
035: * JDBCStoreEntityCommand updates the row with the new state.
036: * In the event that no field is dirty the command just returns.
037: * Note: read-only fields are never considered dirty.
038: *
039: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
040: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
041: * @author <a href="mailto:marc.fleury@telkel.com">Marc Fleury</a>
042: * @author <a href="mailto:shevlandj@kpi.com.au">Joe Shevland</a>
043: * @author <a href="mailto:justin@j-m-f.demon.co.uk">Justin Forder</a>
044: * @author <a href="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
045: * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
046: * @version $Revision: 57209 $
047: */
048: public final class JDBCStoreEntityCommand {
049: private final JDBCEntityBridge entity;
050: private final JDBCFieldBridge[] primaryKeyFields;
051: private final Logger log;
052:
053: public JDBCStoreEntityCommand(JDBCStoreManager manager) {
054: entity = (JDBCEntityBridge) manager.getEntityBridge();
055: primaryKeyFields = entity.getPrimaryKeyFields();
056:
057: // Create the Log
058: log = Logger.getLogger(this .getClass().getName() + "."
059: + manager.getMetaData().getName());
060: }
061:
062: public void execute(EntityEnterpriseContext ctx) {
063: // scheduled for batch cascade-delete instance should not be updated
064: // because foreign key fields could be updated to null and cascade-delete will fail.
065: JDBCEntityBridge.FieldIterator dirtyIterator = entity
066: .getDirtyIterator(ctx);
067: if (!dirtyIterator.hasNext() || entity.isBeingRemoved(ctx)
068: || entity.isScheduledForBatchCascadeDelete(ctx)) {
069: if (log.isTraceEnabled()) {
070: log
071: .trace("Store command NOT executed. Entity is not dirty "
072: + ", is being removed or scheduled for *batch* cascade delete: pk="
073: + ctx.getId());
074: }
075: return;
076: }
077:
078: // generate sql
079: StringBuffer sql = new StringBuffer(200);
080: sql.append(SQLUtil.UPDATE).append(
081: entity.getQualifiedTableName()).append(SQLUtil.SET);
082: SQLUtil.getSetClause(dirtyIterator, sql).append(SQLUtil.WHERE);
083: SQLUtil.getWhereClause(primaryKeyFields, sql);
084:
085: boolean hasLockedFields = entity.hasLockedFields(ctx);
086: JDBCEntityBridge.FieldIterator lockedIterator = null;
087: if (hasLockedFields) {
088: lockedIterator = entity.getLockedIterator(ctx);
089: while (lockedIterator.hasNext()) {
090: sql.append(SQLUtil.AND);
091: JDBCCMPFieldBridge field = lockedIterator.next();
092: if (field.getLockedValue(ctx) == null) {
093: SQLUtil.getIsNullClause(false, field, "", sql);
094: lockedIterator.remove();
095: } else {
096: SQLUtil.getWhereClause(field, sql);
097: }
098: }
099: }
100:
101: Connection con = null;
102: PreparedStatement ps = null;
103: int rowsAffected = 0;
104: try {
105: // create the statement
106: if (log.isDebugEnabled()) {
107: log.debug("Executing SQL: " + sql);
108: }
109:
110: // get the connection
111: con = entity.getDataSource().getConnection();
112: ps = con.prepareStatement(sql.toString());
113:
114: // SET: set the dirty fields parameters
115: int index = 1;
116: dirtyIterator.reset();
117: while (dirtyIterator.hasNext()) {
118: index = dirtyIterator.next().setInstanceParameters(ps,
119: index, ctx);
120: }
121:
122: // WHERE: set primary key fields
123: index = entity.setPrimaryKeyParameters(ps, index, ctx
124: .getId());
125:
126: // WHERE: set optimistically locked field values
127: if (hasLockedFields) {
128: lockedIterator.reset();
129: while (lockedIterator.hasNext()) {
130: JDBCCMPFieldBridge field = lockedIterator.next();
131: Object value = field.getLockedValue(ctx);
132: index = field.setArgumentParameters(ps, index,
133: value);
134: }
135: }
136:
137: // execute statement
138: rowsAffected = ps.executeUpdate();
139: } catch (EJBException e) {
140: throw e;
141: } catch (Exception e) {
142: throw new EJBException("Store failed", e);
143: } finally {
144: JDBCUtil.safeClose(ps);
145: JDBCUtil.safeClose(con);
146: }
147:
148: // check results
149: if (rowsAffected != 1) {
150: throw new EJBException(
151: "Update failed. Expected one affected row: rowsAffected="
152: + rowsAffected + ", id=" + ctx.getId());
153: }
154:
155: // Mark the updated fields as clean.
156: dirtyIterator.reset();
157: while (dirtyIterator.hasNext()) {
158: dirtyIterator.next().setClean(ctx);
159: }
160: }
161: }
|