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 <clinton.be...@gmail.com> 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 <ellis....@gmail.com> wrote: >> >> 2009/12/31 Chris Reeves <ch...@ev-soft.net>: >> > 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: user-java-unsubscr...@ibatis.apache.org >> For additional commands, e-mail: user-java-h...@ibatis.apache.org >> > >
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: user-java-unsubscr...@ibatis.apache.org For additional commands, e-mail: user-java-h...@ibatis.apache.org