git-svn-id: https://samskivert.googlecode.com/svn/trunk@652 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
//
|
||||||
|
// $Id: FieldMask.java,v 1.1 2002/03/15 01:06:03 mdb Exp $
|
||||||
|
//
|
||||||
|
// samskivert library - useful routines for java programs
|
||||||
|
// Copyright (C) 2001 Michael Bayne
|
||||||
|
//
|
||||||
|
// This library 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.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.samskivert.jdbc.jora;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides support for doing partial updates to objects in a JORA table.
|
||||||
|
* A field mask can be obtained for a particular table, fields marked as
|
||||||
|
* modified and the object subsequently updated in a fairly
|
||||||
|
* straightforward manner:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* // updating parts of a table that contains User objects
|
||||||
|
* User user = // load user object from table
|
||||||
|
* FieldMask mask = table.getFieldMask();
|
||||||
|
* user.firstName = newFirstName;
|
||||||
|
* mask.setModified("firstName");
|
||||||
|
* user.lastName = newLastName;
|
||||||
|
* mask.setModified("lastName");
|
||||||
|
* table.update(user, mask);
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class FieldMask
|
||||||
|
implements Cloneable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a field mask for a {@link Table} that uses the supplied
|
||||||
|
* field descriptors.
|
||||||
|
*/
|
||||||
|
public FieldMask (FieldDescriptor[] descrips)
|
||||||
|
{
|
||||||
|
// create a mapping from field name to descriptor index
|
||||||
|
_descripMap = new HashMap();
|
||||||
|
int dcount = descrips.length;
|
||||||
|
for (int i = 0; i < dcount; i++) {
|
||||||
|
_descripMap.put(descrips[i].field.getName(), new Integer(i));
|
||||||
|
}
|
||||||
|
// create our modified flags
|
||||||
|
_modified = new boolean[dcount];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the field with the specified index is modified.
|
||||||
|
*/
|
||||||
|
public final boolean isModified (int index)
|
||||||
|
{
|
||||||
|
return _modified[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the specified field as modified.
|
||||||
|
*/
|
||||||
|
public void setModified (String fieldName)
|
||||||
|
{
|
||||||
|
Integer index = (Integer)_descripMap.get(fieldName);
|
||||||
|
if (index == null) {
|
||||||
|
String errmsg = "";
|
||||||
|
throw new IllegalArgumentException(errmsg);
|
||||||
|
}
|
||||||
|
_modified[index.intValue()] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears out the modification state of the fields in this mask.
|
||||||
|
*/
|
||||||
|
public void clear ()
|
||||||
|
{
|
||||||
|
Arrays.fill(_modified, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a copy of this field mask, with all fields set to
|
||||||
|
* not-modified.
|
||||||
|
*/
|
||||||
|
public Object clone ()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
FieldMask mask = (FieldMask)super.clone();
|
||||||
|
mask._modified = new boolean[_modified.length];
|
||||||
|
return mask;
|
||||||
|
} catch (CloneNotSupportedException cnse) {
|
||||||
|
throw new RuntimeException("Oh god, the clones!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Modified flags for each field of an object in this table. */
|
||||||
|
protected boolean[] _modified;
|
||||||
|
|
||||||
|
/** A mapping from field names to field descriptor index. */
|
||||||
|
protected HashMap _descripMap;
|
||||||
|
}
|
||||||
@@ -254,7 +254,7 @@ public class Table {
|
|||||||
sql += ")";
|
sql += ")";
|
||||||
insertStmt = session.connection.prepareStatement(sql);
|
insertStmt = session.connection.prepareStatement(sql);
|
||||||
}
|
}
|
||||||
bindUpdateVariables(insertStmt, obj);
|
bindUpdateVariables(insertStmt, obj, null);
|
||||||
insertStmt.executeUpdate();
|
insertStmt.executeUpdate();
|
||||||
insertStmt.clearParameters();
|
insertStmt.clearParameters();
|
||||||
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
||||||
@@ -297,7 +297,7 @@ public class Table {
|
|||||||
insertStmt = session.connection.prepareStatement(sql);
|
insertStmt = session.connection.prepareStatement(sql);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < objects.length; i++) {
|
for (int i = 0; i < objects.length; i++) {
|
||||||
bindUpdateVariables(insertStmt, objects[i]);
|
bindUpdateVariables(insertStmt, objects[i], null);
|
||||||
insertStmt.addBatch();
|
insertStmt.addBatch();
|
||||||
}
|
}
|
||||||
insertStmt.executeBatch();
|
insertStmt.executeBatch();
|
||||||
@@ -305,6 +305,15 @@ public class Table {
|
|||||||
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a field mask that can be configured and used to update
|
||||||
|
* subsets of entire objects via calls to {@link
|
||||||
|
* #update(Object,FieldMask)}.
|
||||||
|
*/
|
||||||
|
public FieldMask getFieldMask ()
|
||||||
|
{
|
||||||
|
return (FieldMask)fMask.clone();
|
||||||
|
}
|
||||||
|
|
||||||
/** Update record in the table using table's primary key to locate
|
/** Update record in the table using table's primary key to locate
|
||||||
* record in the table and values of fields of specified object <I>obj</I>
|
* record in the table and values of fields of specified object <I>obj</I>
|
||||||
* to alter record fields.
|
* to alter record fields.
|
||||||
@@ -313,11 +322,30 @@ public class Table {
|
|||||||
* updated record fields
|
* updated record fields
|
||||||
*
|
*
|
||||||
* @return number of objects actually updated
|
* @return number of objects actually updated
|
||||||
*/
|
*/
|
||||||
public int update(Object obj)
|
public int update(Object obj)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
return update(obj, session);
|
return update(obj, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update record in the table using table's primary key to locate
|
||||||
|
* record in the table and values of fields of specified object <I>obj</I>
|
||||||
|
* to alter record fields. Only the fields marked as modified in the
|
||||||
|
* supplied field mask will be updated in the database.
|
||||||
|
*
|
||||||
|
* @param obj object specifing value of primary key and new values of
|
||||||
|
* updated record fields
|
||||||
|
* @param mask a {@link FieldMask} instance configured to indicate
|
||||||
|
* which of the object's fields are modified and should be written to
|
||||||
|
* the database.
|
||||||
|
*
|
||||||
|
* @return number of objects actually updated
|
||||||
|
*/
|
||||||
|
public int update(Object obj, FieldMask mask)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
return update(obj, mask, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update record in the table using table's primary key to locate
|
/** Update record in the table using table's primary key to locate
|
||||||
@@ -332,6 +360,26 @@ public class Table {
|
|||||||
*/
|
*/
|
||||||
public synchronized int update(Object obj, Session session)
|
public synchronized int update(Object obj, Session session)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
|
{
|
||||||
|
return update(obj, null, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update record in the table using table's primary key to locate
|
||||||
|
* record in the table and values of fields of specified object <I>obj</I>
|
||||||
|
* to alter record fields. Only the fields marked as modified in the
|
||||||
|
* supplied field mask will be updated in the database.
|
||||||
|
*
|
||||||
|
* @param obj object specifing value of primary key and new values of
|
||||||
|
* updated record fields
|
||||||
|
* @param mask a {@link FieldMask} instance configured to indicate
|
||||||
|
* which of the object's fields are modified and should be written to
|
||||||
|
* the database.
|
||||||
|
* @param session user database session
|
||||||
|
*
|
||||||
|
* @return number of objects actually updated
|
||||||
|
*/
|
||||||
|
public synchronized int update(Object obj, FieldMask mask, Session session)
|
||||||
|
throws SQLException
|
||||||
{
|
{
|
||||||
if (primaryKeys == null) {
|
if (primaryKeys == null) {
|
||||||
throw new NoPrimaryKeyError(this);
|
throw new NoPrimaryKeyError(this);
|
||||||
@@ -342,21 +390,32 @@ public class Table {
|
|||||||
int nUpdated = 0;
|
int nUpdated = 0;
|
||||||
// try {
|
// try {
|
||||||
checkConnection(session);
|
checkConnection(session);
|
||||||
if (updateStmt == null) {
|
PreparedStatement ustmt;
|
||||||
String sql = "update " + name + " set " + listOfAssignments
|
// if we have a field mask, we need to create a custom update
|
||||||
+ " where " + primaryKeys[0] + " = ?";
|
// statement
|
||||||
for (int i = 1; i < primaryKeys.length; i++) {
|
if (mask != null) {
|
||||||
sql += " and " + primaryKeys[i] + " = ?";
|
String sql = "update " + name + " set " +
|
||||||
}
|
buildListOfAssignments(mask)
|
||||||
updateStmt = session.connection.prepareStatement(sql);
|
+ buildUpdateWhere();
|
||||||
}
|
ustmt = session.connection.prepareStatement(sql);
|
||||||
|
} else {
|
||||||
|
// otherwise we can use our "full-object-update" statement
|
||||||
|
if (updateStmt == null) {
|
||||||
|
String sql = "update " + name + " set " + listOfAssignments
|
||||||
|
+ buildUpdateWhere();
|
||||||
|
updateStmt = session.connection.prepareStatement(sql);
|
||||||
|
}
|
||||||
|
ustmt = updateStmt;
|
||||||
|
}
|
||||||
|
// bind the update variables
|
||||||
|
int column = bindUpdateVariables(ustmt, obj, mask);
|
||||||
|
// bind the keys
|
||||||
for (int i = 0; i < primaryKeys.length; i++) {
|
for (int i = 0; i < primaryKeys.length; i++) {
|
||||||
fields[primaryKeyIndices[i]].bindVariable(updateStmt, obj,
|
int fidx = primaryKeyIndices[i];
|
||||||
nColumns+i+1);
|
fields[fidx].bindVariable(ustmt, obj, column+i+1);
|
||||||
}
|
}
|
||||||
bindUpdateVariables(updateStmt, obj);
|
nUpdated = ustmt.executeUpdate();
|
||||||
nUpdated = updateStmt.executeUpdate();
|
ustmt.clearParameters();
|
||||||
updateStmt.clearParameters();
|
|
||||||
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
// } catch(SQLException ex) { session.handleSQLException(ex); }
|
||||||
return nUpdated;
|
return nUpdated;
|
||||||
}
|
}
|
||||||
@@ -400,19 +459,16 @@ public class Table {
|
|||||||
checkConnection(session);
|
checkConnection(session);
|
||||||
if (updateStmt == null) {
|
if (updateStmt == null) {
|
||||||
String sql = "update " + name + " set " + listOfAssignments
|
String sql = "update " + name + " set " + listOfAssignments
|
||||||
+ " where " + primaryKeys[0] + " = ?";
|
+ buildUpdateWhere();
|
||||||
for (int i = 1; i < primaryKeys.length; i++) {
|
|
||||||
sql += " and " + primaryKeys[i] + " = ?";
|
|
||||||
}
|
|
||||||
updateStmt = session.connection.prepareStatement(sql);
|
updateStmt = session.connection.prepareStatement(sql);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < objects.length; i++) {
|
for (int i = 0; i < objects.length; i++) {
|
||||||
|
int column = bindUpdateVariables(updateStmt, objects[i], null);
|
||||||
for (int j = 0; j < primaryKeys.length; j++) {
|
for (int j = 0; j < primaryKeys.length; j++) {
|
||||||
fields[primaryKeyIndices[j]].bindVariable(updateStmt,
|
int fidx = primaryKeyIndices[j];
|
||||||
objects[i],
|
fields[fidx].bindVariable(
|
||||||
nColumns+1+j);
|
updateStmt, objects[i], column+1+j);
|
||||||
}
|
}
|
||||||
bindUpdateVariables(updateStmt, objects[i]);
|
|
||||||
updateStmt.addBatch();
|
updateStmt.addBatch();
|
||||||
}
|
}
|
||||||
int rc[] = updateStmt.executeBatch();
|
int rc[] = updateStmt.executeBatch();
|
||||||
@@ -571,6 +627,7 @@ public class Table {
|
|||||||
|
|
||||||
static private Class serializableClass;
|
static private Class serializableClass;
|
||||||
private FieldDescriptor[] fields;
|
private FieldDescriptor[] fields;
|
||||||
|
private FieldMask fMask;
|
||||||
|
|
||||||
private int nFields; // length of "fields" array
|
private int nFields; // length of "fields" array
|
||||||
private int nColumns; // number of atomic fields in "fields" array
|
private int nColumns; // number of atomic fields in "fields" array
|
||||||
@@ -620,6 +677,7 @@ public class Table {
|
|||||||
nFields = buildFieldsList(fieldsVector, cls, "");
|
nFields = buildFieldsList(fieldsVector, cls, "");
|
||||||
fields = new FieldDescriptor[nFields];
|
fields = new FieldDescriptor[nFields];
|
||||||
fieldsVector.copyInto(fields);
|
fieldsVector.copyInto(fields);
|
||||||
|
fMask = new FieldMask(fields);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
constructor = cls.getDeclaredConstructor(new Class[0]);
|
constructor = cls.getDeclaredConstructor(new Class[0]);
|
||||||
@@ -846,6 +904,24 @@ public class Table {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final String buildListOfAssignments (FieldMask mask)
|
||||||
|
{
|
||||||
|
StringBuffer sql = new StringBuffer();
|
||||||
|
int fcount = fields.length;
|
||||||
|
for (int i = 0; i < fcount; i++) {
|
||||||
|
// skip non-modified fields
|
||||||
|
if (!mask.isModified(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// separate fields by a comma
|
||||||
|
if (sql.length() > 0) {
|
||||||
|
sql.append(",");
|
||||||
|
}
|
||||||
|
// append the necessary SQL to update this column
|
||||||
|
sql.append(fields[i].name).append("=?");
|
||||||
|
}
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
protected final Object load(ResultSet result) throws SQLException {
|
protected final Object load(ResultSet result) throws SQLException {
|
||||||
Object obj;
|
Object obj;
|
||||||
@@ -887,11 +963,12 @@ public class Table {
|
|||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void bindUpdateVariables(PreparedStatement pstmt,
|
protected final int bindUpdateVariables(PreparedStatement pstmt,
|
||||||
Object obj)
|
Object obj,
|
||||||
|
FieldMask mask)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
bindUpdateVariables(pstmt, obj, 0, nFields, 0);
|
return bindUpdateVariables(pstmt, obj, 0, nFields, 0, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void bindQueryVariables(PreparedStatement pstmt,
|
protected final void bindQueryVariables(PreparedStatement pstmt,
|
||||||
@@ -908,6 +985,16 @@ public class Table {
|
|||||||
result.updateRow();
|
result.updateRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final String buildUpdateWhere()
|
||||||
|
{
|
||||||
|
StringBuffer sql = new StringBuffer();
|
||||||
|
sql.append(" where ").append(primaryKeys[0]).append(" = ?");
|
||||||
|
for (int i = 1; i < primaryKeys.length; i++) {
|
||||||
|
sql.append(" and ").append(primaryKeys[i]).append(" = ?");
|
||||||
|
}
|
||||||
|
return sql.toString();
|
||||||
|
}
|
||||||
|
|
||||||
protected final String buildQueryList(Object qbe)
|
protected final String buildQueryList(Object qbe)
|
||||||
{
|
{
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
@@ -919,13 +1006,18 @@ public class Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final int bindUpdateVariables(PreparedStatement pstmt, Object obj,
|
private final int bindUpdateVariables(PreparedStatement pstmt, Object obj,
|
||||||
int i, int end, int column)
|
int i, int end, int column,
|
||||||
|
FieldMask mask)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
while (i < end) {
|
while (i < end) {
|
||||||
FieldDescriptor fd = fields[i++];
|
FieldDescriptor fd = fields[i++];
|
||||||
Object comp = null;
|
Object comp = null;
|
||||||
|
// skip non-modified fields
|
||||||
|
if (mask != null && !mask.isModified(i-1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!fd.isBuiltin() && (comp = fd.field.get(obj)) == null) {
|
if (!fd.isBuiltin() && (comp = fd.field.get(obj)) == null) {
|
||||||
if (fd.isCompound()) {
|
if (fd.isCompound()) {
|
||||||
int nComponents = fd.outType-FieldDescriptor.tCompound;
|
int nComponents = fd.outType-FieldDescriptor.tCompound;
|
||||||
@@ -944,8 +1036,8 @@ public class Table {
|
|||||||
} else {
|
} else {
|
||||||
if (!fd.bindVariable(pstmt, obj, ++column)) {
|
if (!fd.bindVariable(pstmt, obj, ++column)) {
|
||||||
int nComponents = fd.outType-FieldDescriptor.tCompound;
|
int nComponents = fd.outType-FieldDescriptor.tCompound;
|
||||||
column = bindUpdateVariables(pstmt, comp,
|
column = bindUpdateVariables(
|
||||||
i,i+nComponents,column-1);
|
pstmt, comp, i, i+nComponents,column-1, mask);
|
||||||
i += nComponents;
|
i += nComponents;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user