Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/ConvertHelper.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/ConvertHelper.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/ConvertHelper.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/ConvertHelper.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,92 @@ +package org.apache.ojb.broker.util; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import org.apache.commons.lang.BooleanUtils; +import org.apache.ojb.broker.util.logging.Logger; +import org.apache.ojb.broker.util.logging.LoggerFactory; + +/** + * Helper class to convert objects (e.g. String to Integer). + * + * @version $Id: $ + */ +public class ConvertHelper +{ + private static Logger log = LoggerFactory.getLogger(ConvertHelper.class); + + public static Integer toInteger(String value) throws NumberFormatException + { + try + { + return value != null ? new Integer(value) : null; + } + catch(NumberFormatException e) + { + log.error("Can't convert String '" + value + "' to Integer"); + throw e; + } + } + + public static int toIntegerPrimitiv(String value) throws NumberFormatException + { + try + { + return value != null ? Integer.parseInt(value) : 0; + } + catch(NumberFormatException e) + { + log.error("Can't convert String '" + value + "' to Integer"); + throw e; + } + } + + public static Long toLong(String value) throws NumberFormatException + { + try + { + return value != null ? new Long(value) : null; + } + catch(NumberFormatException e) + { + log.error("Can't convert String '" + value + "' to Long"); + throw e; + } + } + + public static long toLongPrimitiv(String value) throws NumberFormatException + { + try + { + return value != null ? Long.parseLong(value) : 0; + } + catch(NumberFormatException e) + { + log.error("Can't convert String '" + value + "' to Long"); + throw e; + } + } + + public static Boolean toBoolean(String value) + { + return BooleanUtils.toBooleanObject(value); + } + + public static boolean toBooleanPrimitiv(String value) + { + return BooleanUtils.toBoolean(value); + } +}
Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/IdentityHashSet.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/IdentityHashSet.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/IdentityHashSet.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/IdentityHashSet.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,94 @@ +package org.apache.ojb.broker.util; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.apache.commons.collections.map.IdentityMap; + +/** + * This is a object identity based [EMAIL PROTECTED] java.util.Set} implementation. + * + * @version $Id: $ + */ +public class IdentityHashSet extends AbstractSet implements Set +{ + private static final Object DUMMY = new Object(); + private org.apache.commons.collections.map.IdentityMap map; + + public IdentityHashSet() + { + this(null); + } + + public IdentityHashSet(Collection c) + { + map = new org.apache.commons.collections.map.IdentityMap(); + if(c != null) addAll(c); + } + + public Iterator iterator() + { + return map.keySet().iterator(); + } + + public int size() + { + return map.size(); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public boolean contains(Object o) + { + return map.containsKey(o); + } + + public boolean add(Object o) + { + return map.put(o, DUMMY) == null; + } + + public boolean remove(Object o) + { + return map.remove(o) == DUMMY; + } + + public void clear() + { + map.clear(); + } + + public Object clone() + { + try + { + IdentityHashSet newSet = (IdentityHashSet) super.clone(); + newSet.map = (IdentityMap) map.clone(); + return newSet; + } + catch(CloneNotSupportedException e) + { + throw new RuntimeException("Unexpected error", e); + } + } +} Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/WeakIdentityList.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/WeakIdentityList.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/WeakIdentityList.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/WeakIdentityList.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,171 @@ +package org.apache.ojb.broker.util; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.lang.Object; +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.AbstractList; +import java.util.List; + +/** + * A object identity based (use '==' instead of 'equals' to compare objects) [EMAIL PROTECTED] java.util.List} + * with <em>weak</em> object entries (internal use of [EMAIL PROTECTED] java.lang.ref.WeakReference}). + * + * @version $Id: $ + */ +public class WeakIdentityList extends AbstractList +{ + private List list; + + /** + * Reference queue for weak references + */ + private final ReferenceQueue queue = new ReferenceQueue(); + + public WeakIdentityList() + { + this(null, 20); + } + + public WeakIdentityList(int initialCapacity) + { + this(null, initialCapacity); + } + + public WeakIdentityList(Collection c) + { + this(c, c != null ? c.size() + 20 : 20); + } + + public WeakIdentityList(Collection c, int initialCapacity) + { + list = new ArrayList(initialCapacity); + if(c != null) + { + addAll(c); + } + } + + public Object get(int index) + { + Entry result = (Entry) list.get(index); + // pollQueue() call could corrupt internal used Iterator + return unwrap(result); + } + + public Iterator iterator() + { + // poll before return Iterator + pollQueue(); + return super.iterator(); + } + + public int size() + { + pollQueue(); + return list.size(); + } + + public Object set(int index, Object element) + { + Entry result = (Entry) list.set(index, wrap(element)); + // poll queue after set of object to avoid index error + pollQueue(); + return unwrap(result); + } + + public void add(int index, Object element) + { + list.add(index, wrap(element)); + // poll queue after add of object to avoid index error + pollQueue(); + } + + public Object remove(int index) + { + return list.remove(index); + } + + /** + * Poll reference queue. + */ + private void pollQueue() + { + Object ref; + while((ref = queue.poll()) != null) + { + remove(ref); + } + } + + private Entry wrap(Object obj) + { + if(obj == null) + { + throw new NullPointerException("Adding 'null' entries to list is not allowed"); + } + return new Entry(obj, queue); + } + + private Object unwrap(Entry e) + { + return e != null ? e.get() : null; + } + + static class Entry extends WeakReference + { + public Entry(Object referent) + { + super(referent); + } + + public Entry(Object referent, ReferenceQueue q) + { + super(referent, q); + } + + public boolean equals(Object other) + { + boolean result = false; + Object current = get(); + //Object other = obj != null ? ((WeakReference) obj).get() : null; + if(current != null) + { + if(other != null) + { + result = current == other; + } + } + else + { + if(other == null) + { + result = true; + } + } + return result; + } + + public String toString() + { + return get() != null ? get().toString() : null; + } + } +} Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/collections/RemovalAwareVector.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/collections/RemovalAwareVector.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/collections/RemovalAwareVector.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/collections/RemovalAwareVector.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,138 @@ +package org.apache.ojb.broker.util.collections; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +/** + * This class + * + * @version $Id: $ + */ +public class RemovalAwareVector extends ManageableVector implements TrackingCollection +{ + private Collection deletedObjects = new HashSet(); + private Collection newObjects = new HashSet(); + + public Collection getDeletedObjects() + { + return deletedObjects; + } + + public Collection getNewObjects() + { + return newObjects; + } + + public void clearDeletedObjects() + { + deletedObjects.clear(); + } + + public void clearNewObjects() + { + newObjects.clear(); + } + + + + public void add(int index, Object element) + { + newObjects.add(element); + super.add(index, element); + } + + public synchronized boolean add(Object o) + { + newObjects.add(o); + return super.add(o); + } + + public synchronized boolean addAll(Collection c) + { + newObjects.addAll(c); + return super.addAll(c); + } + + public synchronized boolean addAll(int index, Collection c) + { + newObjects.addAll(c); + return super.addAll(index, c); + } + + public synchronized void addElement(Object obj) + { + newObjects.add(obj); + super.addElement(obj); + } + + public synchronized Object remove(int index) + { + deletedObjects.add(get(index)); + return super.remove(index); + } + + public synchronized boolean removeAll(Collection c) + { + Iterator e = iterator(); + while(e.hasNext()) + { + Object o = e.next(); + if(c.contains(o)) + { + deletedObjects.add(o); + } + } + return super.removeAll(c); + } + + public synchronized void removeAllElements() + { + Iterator it = iterator(); + while(it.hasNext()) + { + deletedObjects.add(it.next()); + } + super.removeAllElements(); + } + + public synchronized boolean removeElement(Object obj) + { + boolean result = super.removeElement(obj); + if(result) deletedObjects.add(obj); + return result; + } + + public synchronized void removeElementAt(int index) + { + deletedObjects.add(get(index)); + super.removeElementAt(index); + } + + protected void removeRange(int fromIndex, int toIndex) + { + // not supported + throw new UnsupportedOperationException("Not supported"); + } + + public synchronized boolean retainAll(Collection c) + { + // not supported + throw new UnsupportedOperationException("Not supported"); + } +} Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/PerFieldManager.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/PerFieldManager.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/PerFieldManager.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/PerFieldManager.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,132 @@ +package org.apache.ojb.broker.util.sequence; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.util.Map; + +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.ojb.broker.PersistenceBrokerInternal; +import org.apache.ojb.broker.accesslayer.JdbcAccess; +import org.apache.ojb.broker.metadata.ClassDescriptor; +import org.apache.ojb.broker.metadata.FieldDescriptor; +import org.apache.ojb.broker.metadata.SequenceDescriptor; +import org.apache.ojb.broker.util.logging.Logger; +import org.apache.ojb.broker.util.logging.LoggerFactory; + +/** + * A sequence manager implementation which handle per field sequence manager + * declarations. + * + * @version $Id: $ + */ +public class PerFieldManager implements SequenceManager +{ + private Logger log = LoggerFactory.getLogger(this.getClass()); + + private Map fieldToManagerMap; + private SequenceManager defaultManager; + + /** + * Constructor. + * @param defaultManager The default sequence manager instance used if no specific + * sequence manager ([EMAIL PROTECTED] org.apache.ojb.broker.metadata.SequenceDescriptor}) is set + * for the [EMAIL PROTECTED] org.apache.ojb.broker.metadata.FieldDescriptor}. + */ + public PerFieldManager(SequenceManager defaultManager) + { + setDefaultManager(defaultManager); + log.info("Init per field sequence manager. Current set default manager is " + defaultManager); + fieldToManagerMap = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.HARD); + } + + /** + * Get the default manager. + */ + public SequenceManager getDefaultManager() + { + return defaultManager; + } + + /** + * Set the default manager instance used for all [EMAIL PROTECTED] org.apache.ojb.broker.metadata.FieldDescriptor} + * without specific [EMAIL PROTECTED] org.apache.ojb.broker.metadata.SequenceDescriptor}. + * + * @param defaultManager The default [EMAIL PROTECTED] SequenceManager} instance. + */ + public void setDefaultManager(SequenceManager defaultManager) + { + if(log.isDebugEnabled()) log.debug("Set default sequence manager: " + defaultManager); + this.defaultManager = defaultManager; + } + + public Object getUniqueValue(PersistenceBrokerInternal broker, FieldDescriptor field) throws SequenceManagerException + { + SequenceManager sm = lookup(broker, field); + return sm.getUniqueValue(broker, field); + } + + public void afterStore(PersistenceBrokerInternal broker, JdbcAccess dbAccess, ClassDescriptor cld, Object obj) throws SequenceManagerException + { + FieldDescriptor[] pks = cld.getAutoIncrementFields(); + FieldDescriptor result; + for(int i = 0; i < pks.length; i++) + { + result = pks[i]; + SequenceManager sm = lookup(broker, result); + if(sm != null) + { + sm.afterStore(broker, dbAccess, cld, obj); + } + } + } + + private SequenceManager lookup(PersistenceBrokerInternal broker, FieldDescriptor field) + { + SequenceManager sm = (SequenceManager) fieldToManagerMap.get(field); + if(sm == null && field.isAutoIncrement()) + { + SequenceDescriptor sd = field.getSequenceDescriptor(); + if(sd != null) + { + if(log.isDebugEnabled()) + { + ClassDescriptor cld = field.getClassDescriptor(); + log.debug("Create field specific sequence manager for field '" + + field.getPersistentField().getName() + + "' of class '" + + (cld != null ? cld.getClassNameOfObject() : "null") + + "', using sequence descriptor=" + sd); + } + sm = SequenceManagerHelper.createManager(broker.serviceConnectionManager().getSupportedPlatform(), sd); + } + else + { + if(log.isDebugEnabled()) + { + ClassDescriptor cld = field.getClassDescriptor(); + log.debug("Use default sequence manager for field '" + + field.getPersistentField().getName() + + "' of class '" + + (cld != null ? cld.getClassNameOfObject() : "null") + + "'"); + } + sm = defaultManager; + } + fieldToManagerMap.put(field, sm); + } + return sm; + } +} Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerIdentityImpl.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerIdentityImpl.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerIdentityImpl.java (added) +++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerIdentityImpl.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,225 @@ +package org.apache.ojb.broker.util.sequence; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ojb.broker.PersistenceBrokerInternal; +import org.apache.ojb.broker.platforms.Platform; +import org.apache.ojb.broker.accesslayer.JdbcAccess; +import org.apache.ojb.broker.metadata.ClassDescriptor; +import org.apache.ojb.broker.metadata.FieldDescriptor; +import org.apache.ojb.broker.metadata.SequenceDescriptor; +import org.apache.ojb.broker.metadata.fieldaccess.PersistentField; + +/** + * Sequence manager implementation using native database <tt>Identity columns</tt> + * (like MySQL, MSSQL, ...). For proper work some specific metadata settings + * needed: + * <ul> + * <li>field representing the identity column need attribute <code>autoincrement</code> 'true'</li> + * <li>field representing the identity column need attribute <code>access</code> set 'readonly'</li> + * <li>field representing the identity column need attribute <code>primarykey</code> set 'true'</li> + * <li>only possible to declare one identity field per class</li> + * </ul> + * <p/> + * <b>Note:</b> + * Make sure that the DB generated identity columns represent values > 0, because negative values + * intern used by this implementation and 0 could cause problems with primitive FK fields. + * </p> + * <p/> + * Implementation configuration properties: + * <table cellspacing="2" cellpadding="2" border="3" frame="box"> + * <tr> + * <td><strong>Property Key</strong></td> + * <td><strong>Property Values</strong></td> + * </tr> + * <tr> + * <td>no properties to set</td> + * <td> + * <p/> + * </td> + * </tr> + * </table> + * </p> + * <p/> + * <p/> + * <b>Limitations:</b> + * <ul> + * <li>Native key generation is not 'extent aware' + * when extent classes span several tables! Please + * see more in shipped docs 'extents and polymorphism' + * or sequence manager docs. + * </li> + * <li> + * Only positive identity values are allowed (see above). + * </li> + * </ul> + * </p> + * <br/> + * + * @version $Id: SequenceManagerIdentityImpl.java 432648 2006-08-18 19:18:37 +0200 (Fr, 18 Aug 2006) arminw $ + */ +public class SequenceManagerIdentityImpl extends AbstractSequenceManager +{ + private Log log = LogFactory.getLog(SequenceManagerIdentityImpl.class); + + /* + TODO: + 1. Find a better solution (if possible) for this problem + We need this dummy field to return a negative long value + on getUniqueLong(...) call. If we return always the same + value, the resulting Identity object was found on cache. + + 2. Problem is that generated oid (by Identity column) + must not begin with 0. + + Use keyword 'volatile' to make decrement of a long value an + atomic operation + */ + private static volatile long tempKey = -1; + + public SequenceManagerIdentityImpl(Platform platform, SequenceDescriptor descriptor) + { + super(platform, descriptor); + } + + public void afterStore(PersistenceBrokerInternal broker, JdbcAccess dbAccess, ClassDescriptor cld, Object obj) + throws SequenceManagerException + { + // if database Identity Columns are used, query the id from database + // thus we have to execute batch entries before + broker.serviceBatchManager().executeBatch(); + FieldDescriptor identityField = extractIdentityColumnField(cld); + if(identityField != null) + { + ifNotReadOnlyFail(identityField); + long newId = getLastInsert(broker, cld, identityField); + setFieldValue(obj, identityField, new Long(newId)); + } + } + + /** + * Gets the identity column descriptor for the given class + * or return <code>null</code> if none defined. + * + * @param cld The class descriptor + * @return The class's identity column or <code>null</code> if it does not have one + */ + private FieldDescriptor extractIdentityColumnField(ClassDescriptor cld) + { + FieldDescriptor[] pkFields = cld.getAutoIncrementFields(); + for(int i = 0; i < pkFields.length; i++) + { + // to find the identity column we search for a autoincrement + // read-only field + if(pkFields[i].isAccessReadOnly()) + { + return pkFields[i]; + } + } + return null; + } + + private void ifNotReadOnlyFail(FieldDescriptor field) throws SequenceManagerException + { + // is field declared as read-only? + if(!field.isAccessReadOnly()) + { + throw new SequenceManagerException("Can't find Identity column: Identity columns/fields need to be declared as" + + " 'autoincrement' with 'readonly' access in field-descriptor"); + } + } + + private long getLastInsert(PersistenceBrokerInternal broker, ClassDescriptor cld, FieldDescriptor field) throws SequenceManagerException + { + long newId = 0; + Statement stmt = null; + if(field != null) + { // an autoinc column exists + try + { + stmt = broker.serviceConnectionManager().getConnection().createStatement(); + ResultSet rs = stmt.executeQuery(lastInsertSelect(broker, cld.getFullTableName())); + if(!rs.next()) + { + throw new SequenceManagerException("Could not find native identifier"); + } + newId = rs.getLong(1); + rs.close(); + if(log.isDebugEnabled()) log.debug("After store - newid=" + newId); + } + catch(Exception e) + { + throw new SequenceManagerException( + "Error while execute query: " + lastInsertSelect(broker, cld.getFullTableName()), e); + } + finally + { + try + { + if(stmt != null) stmt.close(); + } + catch(SQLException e) + { + if(log.isDebugEnabled()) + log.debug("Threw SQLException while in getLastInsert and closing stmt", e); + // ignore it + } + } + } + else + { + throw new SequenceManagerException("No autoincrement field declared, please check repository for " + cld); + } + return newId; + } + + /* + * query for the last insert id. + */ + protected String lastInsertSelect(PersistenceBrokerInternal broker, String tableName) + { + return broker.serviceConnectionManager(). + getSupportedPlatform().getLastInsertIdentityQuery(tableName); + } + + private void setFieldValue(Object obj, FieldDescriptor field, Long identifier) throws SequenceManagerException + { + Object result = field.getJdbcType().sequenceKeyConversion(identifier); + result = field.getFieldConversion().sqlToJava(result); + PersistentField pf = field.getPersistentField(); + pf.set(obj, result); + } + + /** + * returns a negative value + */ + protected long getUniqueLong(PersistenceBrokerInternal broker, FieldDescriptor field) throws SequenceManagerException + { + /* + arminw: + workaround for locking problems of new objects + We need unique 'dummy keys' for new objects before storing. + Variable 'tempKey' is declared volatile, thus decrement should be atomic + */ + return --tempKey; + } +} Added: db/ojb/trunk/src/test/org/apache/ojb/broker/BatchStrategyTest.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/broker/BatchStrategyTest.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/test/org/apache/ojb/broker/BatchStrategyTest.java (added) +++ db/ojb/trunk/src/test/org/apache/ojb/broker/BatchStrategyTest.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,388 @@ +package org.apache.ojb.broker; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.ojb.junit.PBTestCase; +import org.apache.ojb.broker.accesslayer.batch.BatchStrategy; +import org.apache.ojb.broker.accesslayer.batch.BatchManager; +import org.apache.ojb.broker.accesslayer.batch.Batcher; +import org.apache.ojb.broker.accesslayer.batch.BatchListener; +import org.apache.ojb.broker.accesslayer.batch.BatchStrategyDefaultImpl; +import org.apache.ojb.broker.accesslayer.sql.SqlInsertStatement; +import org.apache.ojb.broker.accesslayer.sql.SqlUpdateStatement; +import org.apache.ojb.broker.accesslayer.sql.SqlDeleteByPkStatement; +import org.apache.ojb.broker.core.ValueContainer; +import org.apache.ojb.broker.util.ClassHelper; +import org.apache.ojb.broker.util.logging.Logger; +import org.apache.ojb.broker.metadata.ClassDescriptor; +import org.apache.ojb.broker.platforms.Platform; + +/** + * Test batch strategy implementation. + */ +public class BatchStrategyTest extends PBTestCase +{ + public static void main(String[] args) + { + String[] arr = {BatchStrategyTest.class.getName()}; + junit.textui.TestRunner.main(arr); + } + + public BatchStrategyTest(String testName) + { + super(testName); + } + + public void setUp() throws Exception + { + super.setUp(); + + } + + public void testMultipleObject() throws Exception + { + doMultipleObject(BatchStrategyDefaultImpl.class); + } + + public void doMultipleObject(Class strategyClass) throws Exception + { + TestBatchManager bm = new TestBatchManager(); + BatchStrategy strategy = createStrategy(strategyClass, bm); + bm.setStrategy(strategy); + Platform pf = broker.serviceConnectionManager().getSupportedPlatform(); + Logger log = new NoopLogger(); + + ValueContainer[] empty = new ValueContainer[]{}; + ClassDescriptor cld = broker.getClassDescriptor(Person.class); + ClassDescriptor cldOther = broker.getClassDescriptor(Zoo.class); + + String sqlInsert = (new SqlInsertStatement(pf, log, cld)).getStatement(); + String sqlUpdate = (new SqlUpdateStatement(pf, log, cld)).getStatement(); + String sqlDelete = (new SqlDeleteByPkStatement(pf, log, cld)).getStatement(); + + String sqlInsertOther = (new SqlInsertStatement(pf, log, cldOther)).getStatement(); + String sqlUpdateOther = (new SqlUpdateStatement(pf, log, cldOther)).getStatement(); + String sqlDeleteOther = (new SqlDeleteByPkStatement(pf, log, cldOther)).getStatement(); + + Batcher b_insert = new Batcher.Insert(cld, cld.getInsertProcedure(), sqlInsert); + Batcher b_update = new Batcher.Update(cld, cld.getUpdateProcedure(), sqlUpdate); + Batcher b_delete = new Batcher.Delete(cld, cld.getDeleteProcedure(), sqlDelete); + + Batcher b_insertOther = new Batcher.Insert(cldOther, cldOther.getInsertProcedure(), sqlInsertOther); + Batcher b_updateOther = new Batcher.Update(cldOther, cldOther.getUpdateProcedure(), sqlUpdateOther); + Batcher b_deleteOther = new Batcher.Delete(cldOther, cldOther.getDeleteProcedure(), sqlDeleteOther); + + + bm.add(b_insertOther, empty, 1); + bm.add(b_updateOther, empty, 1); + assertEquals(2, bm.getBatchEntities().size()); + + bm.add(b_update, empty, 1); + bm.add(b_delete, empty, 1); + assertEquals(4, bm.getBatchEntities().size()); + + bm.add(b_deleteOther, empty, 1); + assertEquals(4, bm.getBatchEntities().size()); + + bm.add(b_insert, empty, 1); + assertEquals(2, bm.getBatchEntities().size()); + + bm.executeBatch(); + } + + public void testSingleObject() throws Exception + { + doSingleObject(BatchStrategyDefaultImpl.class); + } + + public void doSingleObject(Class strategyClass) throws Exception + { + TestBatchManager bm = new TestBatchManager(); + BatchStrategy strategy = createStrategy(strategyClass, bm); + bm.setStrategy(strategy); + Platform pf = broker.serviceConnectionManager().getSupportedPlatform(); + Logger log = new NoopLogger(); + + + ValueContainer[] empty = new ValueContainer[]{}; + ClassDescriptor cld = broker.getClassDescriptor(Person.class); + + String sqlInsert = (new SqlInsertStatement(pf, log, cld)).getStatement(); + String sqlUpdate = (new SqlUpdateStatement(pf, log, cld)).getStatement(); + String sqlDelete = (new SqlDeleteByPkStatement(pf, log, cld)).getStatement(); + + Batcher b_insert = new Batcher.Insert(cld, cld.getInsertProcedure(), sqlInsert); + Batcher b_update = new Batcher.Update(cld, cld.getUpdateProcedure(), sqlUpdate); + Batcher b_delete = new Batcher.Delete(cld, cld.getDeleteProcedure(), sqlDelete); + + bm.add(b_insert, empty, 1); + bm.add(b_insert, empty, 1); + bm.add(b_insert, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + + bm.add(b_delete, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + bm.add(b_update, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + + bm.executeBatch(); + + bm.add(b_delete, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + bm.add(b_update, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + bm.add(b_insert, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + + bm.executeBatch(); + + bm.add(b_insert, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + bm.add(b_update, empty, 1); + assertEquals(2, bm.getBatchEntities().size()); + + bm.executeBatch(); + + bm.add(b_update, empty, 1); + assertEquals(1, bm.getBatchEntities().size()); + bm.add(b_delete, empty, 1); + assertEquals(2, bm.getBatchEntities().size()); + } + + private BatchStrategy createStrategy(Class strategy, BatchManager bm) throws Exception + { + return (BatchStrategy) ClassHelper.newInstance( + strategy, BatchManager.class, bm); + } + + static class TestBatchManager implements BatchManager + { + int addCount; + int executeBatchCount; + BatchStrategy strategy; + List batchEntities = new ArrayList(); + + public int getAddCount() + { + return addCount; + } + + public int getExecuteBatchCount() + { + return executeBatchCount; + } + + public List getBatchEntities() + { + return batchEntities; + } + + public void add(Batcher batcher, ValueContainer[] values, int expectedResult) throws SQLException + { + //System.out.println("* add(...)"); + ++addCount; + Batcher entity = checkForExistingBatcher(batcher); + if(entity == null) + { + entity = batcher; + // lookup batch strategy and ask for updated (reordered, reduced, ...) + // list of batch entities + batchEntities = strategy.prepareForBatch(batchEntities, batcher); + } + entity.add(values, expectedResult); + } + + private Batcher checkForExistingBatcher(Batcher newBatcher) + { + for(int i = 0; i < batchEntities.size(); i++) + { + Batcher batcher = (Batcher) batchEntities.get(i); + if(batcher.equals(newBatcher)) + { + return batcher; + } + } + return null; + } + + public void executeBatch() throws PersistenceBrokerException + { + System.out.println("* executeBatch()"); + batchEntities.clear(); + } + + public void executeBatch(Batcher batcher) throws PersistenceBrokerException + { + System.out.println("* executeBatch(batcher): " + batcher); + ++executeBatchCount; + } + + public void cancelBatch() + { + } + + public void addBatchListener(BatchListener listener) + { + } + + public void removeBatchListener(BatchListener listener) + { + } + + public void reset() + { + } + + public void setBatchMode(boolean mode) + { + } + + public boolean isBatchMode() + { + return false; + } + + public void setBatchSupportOptimisticLocking(boolean batchOptimisticLocking) + { + } + + public boolean getBatchSupportOptimisticLocking() + { + return false; + } + + public void setBatchLimit(int limit) + { + } + + public int getBatchLimit() + { + return 1000; + } + + public BatchStrategy getStrategy() + { + return strategy; + } + + public void setStrategy(BatchStrategy strategy) + { + this.strategy = strategy; + } + } + + static class NoopLogger implements Logger + { + public void debug(Object pObject) + { + } + + public void info(Object pObject) + { + } + + public void warn(Object pObject) + { + } + + public void error(Object pObject) + { + } + + public void fatal(Object pObject) + { + } + + public void debug(Object message, Throwable obj) + { + } + + public void info(Object message, Throwable obj) + { + } + + public void warn(Object message, Throwable obj) + { + } + + public void error(Object message, Throwable obj) + { + } + + public void fatal(Object message, Throwable obj) + { + } + + public boolean isEnabledFor(int priority) + { + return false; + } + + public boolean isDebugEnabled() + { + return false; + } + + public String getName() + { + return null; + } + + public void safeDebug(String message, Object obj) + { + } + + public void safeDebug(String message, Object obj, Throwable t) + { + } + + public void safeInfo(String message, Object obj) + { + } + + public void safeInfo(String message, Object obj, Throwable t) + { + } + + public void safeWarn(String message, Object obj) + { + } + + public void safeWarn(String message, Object obj, Throwable t) + { + } + + public void safeError(String message, Object obj) + { + } + + public void safeError(String message, Object obj, Throwable t) + { + } + + public void safeFatal(String message, Object obj) + { + } + + public void safeFatal(String message, Object obj, Throwable t) + { + } + } +} Added: db/ojb/trunk/src/test/org/apache/ojb/broker/OneToOneWithoutFKTest.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/broker/OneToOneWithoutFKTest.java?view=auto&rev=495668 ============================================================================== --- db/ojb/trunk/src/test/org/apache/ojb/broker/OneToOneWithoutFKTest.java (added) +++ db/ojb/trunk/src/test/org/apache/ojb/broker/OneToOneWithoutFKTest.java Fri Jan 12 10:06:09 2007 @@ -0,0 +1,354 @@ +package org.apache.ojb.broker; + +/* Copyright 2002-2006 The Apache Software Foundation + * + * 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. + */ + +import java.io.Serializable; + +import org.apache.ojb.junit.PBTestCase; +import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; +import org.apache.ojb.broker.util.ObjectModification; + +/** + * This class show handling of bidirectional 1:1 reference without + * using a separate FK column (the PK of the main object is the FK too and the both objects use + * the same PK value). + * NOTE: It's not recommended to use 1:1 references without FK column. + * + * @version $Id: $ + */ +public class OneToOneWithoutFKTest extends PBTestCase +{ + public static void main(String[] args) + { + String[] arr = {OneToOneWithoutFKTest.class.getName()}; + junit.textui.TestRunner.main(arr); + } + + public void setUp() throws Exception + { + super.setUp(); + } + + public void tearDown() throws Exception + { + super.tearDown(); + } + + /** + * Handle Person object without using a reference to Passport class. + */ + public void testInsertUpdateDeletePerson() + { + ojbChangeReferenceSetting(Passport.class, "person", + true, ObjectReferenceDescriptor.CASCADE_NONE, ObjectReferenceDescriptor.CASCADE_NONE, true); + ojbChangeReferenceSetting(Person.class, "passport", + true, ObjectReferenceDescriptor.CASCADE_OBJECT, ObjectReferenceDescriptor.CASCADE_OBJECT, true); + String t = "_" + System.currentTimeMillis(); + String name = "Person_testInsertUpdateDeletePerson_" + t; + assertTrue(broker.getClassDescriptor(Person.class).getObjectReferenceDescriptorByName("passport").isLazy()); + assertTrue(broker.getClassDescriptor(Passport.class).getObjectReferenceDescriptorByName("person").isLazy()); + + + Person pers = new Person(name); + + broker.beginTransaction(); + // store plain Person + broker.store(pers, ObjectModification.INSERT); + broker.commitTransaction(); + // clear cache to check DB entry + broker.clearCache(); + // lookup + Identity oid = broker.serviceIdentity().buildIdentity(Person.class, pers.getId()); + Person pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + + broker.beginTransaction(); + pers_new.setName(name + "_updated"); + // store plain Person + broker.store(pers_new, ObjectModification.UPDATE); + broker.commitTransaction(); + // clear cache to check DB entry + broker.clearCache(); + // lookup + oid = broker.serviceIdentity().buildIdentity(Person.class, pers_new.getId()); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + assertEquals(name+"_updated", pers_new.getName()); + // NOTE: You try to access the none existing Passport object + // always null will be returned + assertNull(pers_new.getPassport().getId()); + assertNull(pers_new.getPassport().getNumber()); + assertNull(pers_new.getPassport().getPerson()); + + + broker.beginTransaction(); + // delete plain Person + broker.delete(pers); + broker.commitTransaction(); + // lookup + oid = broker.serviceIdentity().buildIdentity(Person.class, pers_new.getId()); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNull(pers_new); + } + + /** + * Handle Person <---> Passport objects with enabled proxy + */ + public void testInsertUpdateDelete() + { + // metadata settings + // the main object Person use cascade update/delete, the dependend object not. + // For both 1:1 references proxy object are enabled. + ojbChangeReferenceSetting(Passport.class, "person", + true, ObjectReferenceDescriptor.CASCADE_NONE, ObjectReferenceDescriptor.CASCADE_NONE, true); + ojbChangeReferenceSetting(Person.class, "passport", + true, ObjectReferenceDescriptor.CASCADE_OBJECT, ObjectReferenceDescriptor.CASCADE_OBJECT, true); + String t = "_" + System.currentTimeMillis(); + String name = "Person_testInsertUpdateDelete_" + t; + String number = "Passport_testInsertUpdateDelete_" + t; + assertTrue(broker.getClassDescriptor(Person.class).getObjectReferenceDescriptorByName("passport").isLazy()); + assertTrue(broker.getClassDescriptor(Passport.class).getObjectReferenceDescriptorByName("person").isLazy()); + + // insert + Person pers = new Person(name); + Passport pass = new Passport(number); + broker.beginTransaction(); + // store plain Person to assign PK + broker.store(pers); + // set PK/FK of Passport + pass.setId(pers.getId()); + // set references + pers.setPassport(pass); + pass.setPerson(pers); + broker.store(pers); + broker.commitTransaction(); + + // lookup + Identity oid = broker.serviceIdentity().buildIdentity(pers); + Person pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + assertNotNull(pers_new.getPassport()); + + // update + broker.beginTransaction(); + pers_new.setName(name + "_updated"); + pers_new.getPassport().setNumber(number + "_updated"); + broker.store(pers_new); + broker.commitTransaction(); + + // lookup + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + assertNotNull(pers_new.getPassport()); + assertEquals(name + "_updated", pers_new.getName()); + assertEquals(number + "_updated", pers_new.getPassport().getNumber()); + + // update nullify reference, with proxy references enabled, this result + // in unexpected proxy placeholder intead of null reference + broker.beginTransaction(); + broker.delete(pers_new.getPassport()); + pers_new.setPassport(null); + broker.store(pers_new); + broker.commitTransaction(); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + // we alway get a proxy placeholder object + // assertNull(pers_new.getPassport()); + assertNull(pers_new.getPassport().getId()); + assertEquals(name + "_updated", pers_new.getName()); + + // delete + broker.beginTransaction(); + // first delete the object with constraint + broker.delete(pers_new.getPassport()); + // not needed, but this reduce the number of generated queries + pers_new.setPassport(null); + broker.delete(pers_new); + broker.commitTransaction(); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNull(pers_new); + } + + /** + * Handle Person <---> Passport objects withot proxy + */ + public void testInsertUpdateDelete_2() + { + // metadata settings + // the main object Person use cascade update/delete, the dependend object not. + // For both 1:1 references proxy references are disabled. + ojbChangeReferenceSetting(Passport.class, "person", + true, ObjectReferenceDescriptor.CASCADE_NONE, ObjectReferenceDescriptor.CASCADE_NONE, false); + ojbChangeReferenceSetting(Person.class, "passport", + true, ObjectReferenceDescriptor.CASCADE_OBJECT, ObjectReferenceDescriptor.CASCADE_OBJECT, false); + String t = "_" + System.currentTimeMillis(); + String name = "Person_testInsertUpdateDelete_2_" + t; + String number = "Passport_testInsertUpdateDelete_2_" + t; + assertFalse(broker.getClassDescriptor(Person.class).getObjectReferenceDescriptorByName("passport").isLazy()); + assertFalse(broker.getClassDescriptor(Passport.class).getObjectReferenceDescriptorByName("person").isLazy()); + + // insert + Person pers = new Person(name); + Passport pass = new Passport(number); + broker.beginTransaction(); + // store plain Person to assign PK + broker.store(pers); + // set PK/FK of Passport + pass.setId(pers.getId()); + // set references + pers.setPassport(pass); + pass.setPerson(pers); + broker.store(pers); + broker.commitTransaction(); + + // lookup + Identity oid = broker.serviceIdentity().buildIdentity(pers); + Person pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + assertNotNull(pers_new.getPassport()); + assertNotNull(pers_new.getPassport().getPerson()); + + // update + broker.beginTransaction(); + pers_new.setName(name + "_updated"); + pers_new.getPassport().setNumber(number + "_updated"); + broker.store(pers_new); + broker.commitTransaction(); + + // lookup + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNotNull(pers_new); + assertNotNull(pers_new.getPassport()); + assertEquals(name + "_updated", pers_new.getName()); + assertEquals(number + "_updated", pers_new.getPassport().getNumber()); + + // update nullify reference, with proxy references enabled, this result + // in unexpected proxy placeholder intead of null reference + broker.beginTransaction(); + broker.delete(pers_new.getPassport()); + pers_new.setPassport(null); + broker.store(pers_new); + broker.commitTransaction(); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNull(pers_new.getPassport()); + assertEquals(name + "_updated", pers_new.getName()); + + // delete + broker.beginTransaction(); + // first delete the object with constraint + broker.delete(pers_new.getPassport()); + // not needed, but this reduce the number of generated queries + pers_new.setPassport(null); + broker.delete(pers_new); + broker.commitTransaction(); + pers_new = (Person) broker.getObjectByIdentity(oid); + assertNull(pers_new); + } + + + + public static class Person implements Serializable + { + private Integer id; + private String name; + private Passport passport; + + public Person() + { + } + + public Person(String name) + { + this.name = name; + } + + public Integer getId() + { + return id; + } + + public void setId(Integer id) + { + this.id = id; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Passport getPassport() + { + return passport; + } + + public void setPassport(Passport passport) + { + this.passport = passport; + } + } + + public static class Passport implements Serializable + { + private Integer id; + private String number; + private Person person; + + public Passport() + { + } + + public Passport(String number) + { + this.number = number; + } + + public Integer getId() + { + return id; + } + + public void setId(Integer id) + { + this.id = id; + } + + public String getNumber() + { + return number; + } + + public void setNumber(String number) + { + this.number = number; + } + + public Person getPerson() + { + return person; + } + + public void setPerson(Person person) + { + this.person = person; + } + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]