Thanks Clinton; for an excellent mapper that's well designed and for
the pointers.
Here is what I came up with for an object wrapper factory. I don't
really like keeping it in static field on MetaObject, but I couldn't
come up with anything better. I'm not entirely certain the xml
configuration part is done correctly, but it adds
<ObjectWrapperFactory type="com.something.MyFactory" /> in the same
way as ObjectFactory.
I was hoping to be able to subclass BeanWrapper to accomplish my goal
of supporting scala objects, but it looks like it delegates to much to
Reflector and MetaClass. I will post my code for a scala object
wrapper and more generic base class when I complete it, if anyone is
interested.
Thanks, Chris
On Thu, Dec 31, 2009 at 11:00 AM, Clinton Begin <[email protected]> wrote:
> For both cases, I believe all necessary changes would be in the wrappers.
> However, there are places where Map is treated like a special case. But as
> long as you stick to making a peer to the bean wrapper, then you should be
> fine.
>
> While there's no factory class, the MetaObject framework uses a factory
> method w/ delegates rather than a constructor, and is aware of all known
> implementations.
>
> private MetaObject(Object object, ObjectFactory objectFactory) {
> this.originalObject = object;
> this.objectFactory = objectFactory;
> if (object instanceof ObjectWrapper) {
> this.objectWrapper = (ObjectWrapper) object;
> } else if (object instanceof Map) {
> this.objectWrapper = new MapWrapper(this, (Map) object);
> } else {
> this.objectWrapper = new BeanWrapper(this, object);
> }
> }
>
> public static MetaObject forObject(Object object, ObjectFactory
> objectFactory) {
> if (object == null) {
> return NULL_META_OBJECT;
> } else {
> return new MetaObject(object, objectFactory);
> }
> }
>
>
> So perhaps one implementation could be a 3rd party wrapper factory that can
> provide the new delegate. It should be easy for you to code this, but the
> pain in the butt always comes down to where to configure such a thing. I
> suppose just a new root config level element like <MetaClassWrapper
> type="com.something.ScalaObjectWrappperFactory"/> The factory could have a
> method like: isWrapperFor(Class type).
>
> Sounds perfectly possible. Then we could add external support for things
> like dynabeans etc.
>
> Also, for Martin, I _think_ this might be all that is needed to create
> support for custom column translations like: first_name => firstName etc.
>
> Clinton
>
>
>
> On Thu, Dec 31, 2009 at 5:11 AM, Martin Ellis <[email protected]> wrote:
>>
>> 2009/12/31 Chris Reeves <[email protected]>:
>> > I would like to add support for scala objects to iBATIS 3 for a
>> > project I'm working on, and I have a few questions before I dive in
>> > too deep.
>>
>> Sounds interesting.
>> I assume you're referring to the need to call foo_= instead of setFoo.
>>
>> > This would allow the scala support to be added to the
>> > project in a contrib module so the main project would have no
>> > dependencies on the scala libraries, as I will probably use the
>> > ScalaObject marker interface to create the appropriate wrapper. Of
>> > course, if no one else is interested in native scala object support,
>> > this is moot.
>>
>> Not sure I follow. Is there any benefit in casting to ScalaObject? I
>> wonder whether it's possible to reference the ScalaObject interface by
>> name only (hence, no need to link to it).
>>
>> Another option (if you do want to link to the scala libraries) might
>> be to make it an optional dependency. Currently, iBATIS has build
>> dependencies on all sorts of logging frameworks, but they're all
>> optional at runtime. Could do a similar thing with the scala libs (I
>> assume you're thinking about writing the integration in Java).
>>
>> > However, other getter/setter naming strategies that don't conform to
>> > the javabeans spec could also be plugged in if there was a
>> > configurable wrapper factory, which probably isn't very useful to most
>> > java developers, but may be useful for other jvm languages or some
>> > seriously strange legacy cruft.
>>
>> On the other hand, even for code with conventional getter/setter
>> naming, it might be useful for handling different column naming
>> conventions.
>>
>> In the iBATIS app I'm working on, all the database columns have
>> underscore_names, so I've ended up using column aliasing (SELECT
>> foo_bar fooBar, ...) for each column.
>>
>> I never figured out whether there was a better way to do this, but at
>> the time I was looking for a way to implement exactly the 'pluggable'
>> naming strategies you describe.
>>
>>
>> Martin
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [email protected]
>> For additional commands, e-mail: [email protected]
>>
>
>
Index: ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapper.java
===================================================================
--- ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapper.java (revision 0)
+++ ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapper.java (revision 0)
@@ -0,0 +1,10 @@
+package domain.misc;
+
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.wrapper.BeanWrapper;
+
+public class CustomBeanWrapper extends BeanWrapper {
+ public CustomBeanWrapper(MetaObject metaObject, Object object) {
+ super(metaObject, object);
+ }
+}
\ No newline at end of file
Index: ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapperFactory.java
===================================================================
--- ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapperFactory.java (revision 0)
+++ ibatis-3-core/src/test/java/domain/misc/CustomBeanWrapperFactory.java (revision 0)
@@ -0,0 +1,21 @@
+package domain.misc;
+
+import domain.jpetstore.Product;
+
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+
+public class CustomBeanWrapperFactory implements ObjectWrapperFactory {
+ public boolean hasWrapperFor(Object object) {
+ if (object instanceof Product) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
+ return new CustomBeanWrapper(metaObject, object);
+ }
+}
\ No newline at end of file
Index: ibatis-3-core/src/test/java/org/apache/ibatis/reflection/MetaObjectTest.java
===================================================================
--- ibatis-3-core/src/test/java/org/apache/ibatis/reflection/MetaObjectTest.java (revision 894875)
+++ ibatis-3-core/src/test/java/org/apache/ibatis/reflection/MetaObjectTest.java (working copy)
@@ -2,6 +2,11 @@
import domain.jpetstore.Product;
import domain.misc.RichType;
+
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+import domain.misc.CustomBeanWrapper;
+import domain.misc.CustomBeanWrapperFactory;
+
import static org.junit.Assert.*;
import org.junit.Test;
@@ -216,6 +221,26 @@
assertEquals("Clinton", name.get("first"));
assertEquals("1 Some Street", address.get("street"));
}
+
+ @Test
+ public void shouldNotUseObjectWrapperFactoryByDefault() {
+ MetaObject meta = MetaObject.forObject(new Product());
+ assertTrue(!meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
+ }
+
+ @Test
+ public void shouldUseObjectWrapperFactoryWhenSet() {
+ ObjectWrapperFactory old = MetaObject.getObjectWrapperFactory();
+ MetaObject.setObjectWrapperFactory(new CustomBeanWrapperFactory());
+
+ MetaObject meta = MetaObject.forObject(new Product());
+
+ assertTrue(meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
+
+ // Make sure the old default factory is in place and still works
+ MetaObject.setObjectWrapperFactory(old);
+ meta = MetaObject.forObject(new Product());
+ assertTrue(!meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
+ }
-
}
Index: ibatis-3-core/src/main/java/org/apache/ibatis/session/Configuration.java
===================================================================
--- ibatis-3-core/src/main/java/org/apache/ibatis/session/Configuration.java (revision 894875)
+++ ibatis-3-core/src/main/java/org/apache/ibatis/session/Configuration.java (working copy)
@@ -24,6 +24,8 @@
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
@@ -173,7 +175,15 @@
public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
+
+ public ObjectWrapperFactory getObjectWrapperFactory() {
+ return MetaObject.getObjectWrapperFactory();
+ }
+ public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
+ MetaObject.setObjectWrapperFactory(objectWrapperFactory);
+ }
+
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
Index: ibatis-3-core/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java
===================================================================
--- ibatis-3-core/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java (revision 894875)
+++ ibatis-3-core/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java (working copy)
@@ -11,6 +11,7 @@
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.factory.ObjectFactory;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.transaction.TransactionFactory;
@@ -59,6 +60,7 @@
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
+ objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
propertiesElement(root.evalNode("properties"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
@@ -110,6 +112,14 @@
configuration.setObjectFactory(factory);
}
}
+
+ private void objectWrapperFactoryElement(XNode context) throws Exception {
+ if (context != null) {
+ String type = context.getStringAttribute("type");
+ ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
+ configuration.setObjectWrapperFactory(factory);
+ }
+ }
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Index: ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/ObjectWrapperFactory.java
===================================================================
--- ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/ObjectWrapperFactory.java (revision 0)
+++ ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/ObjectWrapperFactory.java (revision 0)
@@ -0,0 +1,11 @@
+package org.apache.ibatis.reflection.wrapper;
+
+import org.apache.ibatis.reflection.MetaObject;
+
+public interface ObjectWrapperFactory {
+
+ boolean hasWrapperFor(Object object);
+
+ ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);
+
+}
\ No newline at end of file
Index: ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/DefaultObjectWrapperFactory.java
===================================================================
--- ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/DefaultObjectWrapperFactory.java (revision 0)
+++ ibatis-3-core/src/main/java/org/apache/ibatis/reflection/wrapper/DefaultObjectWrapperFactory.java (revision 0)
@@ -0,0 +1,15 @@
+package org.apache.ibatis.reflection.wrapper;
+
+import org.apache.ibatis.reflection.MetaObject;
+
+public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {
+
+ public boolean hasWrapperFor(Object object) {
+ return false;
+ }
+
+ public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
+ throw new RuntimeException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
+ }
+
+}
\ No newline at end of file
Index: ibatis-3-core/src/main/java/org/apache/ibatis/reflection/MetaObject.java
===================================================================
--- ibatis-3-core/src/main/java/org/apache/ibatis/reflection/MetaObject.java (revision 894875)
+++ ibatis-3-core/src/main/java/org/apache/ibatis/reflection/MetaObject.java (working copy)
@@ -6,6 +6,8 @@
import org.apache.ibatis.reflection.wrapper.BeanWrapper;
import org.apache.ibatis.reflection.wrapper.MapWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import java.util.Map;
@@ -13,6 +15,8 @@
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
public static final MetaObject NULL_META_OBJECT = new MetaObject(NullObject.class, DEFAULT_OBJECT_FACTORY);
+
+ private static ObjectWrapperFactory objectWrapperFactory;
private Object originalObject;
private ObjectWrapper objectWrapper;
@@ -21,14 +25,33 @@
private MetaObject(Object object, ObjectFactory objectFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
+
+ ObjectWrapperFactory factory = getObjectWrapperFactory();
+
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
+ } else if (factory != null && factory.hasWrapperFor(object)) {
+ this.objectWrapper = factory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
+
+ public static ObjectWrapperFactory getObjectWrapperFactory() {
+ if (objectWrapperFactory == null) {
+ objectWrapperFactory = new DefaultObjectWrapperFactory();
+ }
+
+ return objectWrapperFactory;
+ }
+
+ public static void setObjectWrapperFactory(ObjectWrapperFactory wrapperFactory) {
+ if (wrapperFactory != null) {
+ objectWrapperFactory = wrapperFactory;
+ }
+ }
public static MetaObject forObject(Object object, ObjectFactory objectFactory) {
if (object == null) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]