ibatis typeHandler 转换数据库类型到枚举类型

来源:互联网 发布:帝国cms模板目录 编辑:程序博客网 时间:2024/04/30 04:57

ibatis typeHandler

http://doc.gotomao.com/doc/mybatis/api/org/apache/ibatis/type/EnumOrdinalTypeHandler.html


http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+use+a+Custom+Type+Handler+with+complex+property+or+Type+Safe+Enumeration?decorator=printable

http://mybatis.github.io/mybatis-3/zh/configuration.html



Here is an example of how to set up a custom type handler with a complex property. This example uses the enum features of Java 1.5 but could be used with a Type Safe Enumeration in anything below 1.5.

Lets start by defining the database table

SHAPE {   name    varchar2(25),   color   number(100)   }

As you see the color is stored as a number, but in the class Shape is is a Color object. There are two question that I ran into. First, how do you cleanly map the number stored in the database with the instance of a Color? Second, how can iBatis and custom type handlers help?

To answer the first let me show you the code for the Shape class.

/*   * Shape.java   *   * Created on September 23, 2005   */   package domain;   /**   *   * @author nmaves   */   public class Shape {   public enum Color {   BLUE(1, "0000FF"), RED(2, "FF0000"), GREEN(3, "00FF00");      private int id;   private String rgb;      private Type(int id, String name) {   this(id, name, "no mime type");      }      private Type(int id, String rgb) {   this.id = id;   this. rgb = rgb;   }      public int getId() {   return this.id;   }      public String getRGB() {   return this.name;   }      public static Type getInstance(int i) {   for(Color color : Color.values()) {   if(color.id == i) {   return type;   }   }      throw new IllegalArgumentException("No such Color");   }   }      private String name;   private Color color;      /** Creates a new instance of Frequency */   private Shape(String name, Color color) {   this.color = color;   this.name = name;   }         public String toString() {   return this.name;   }         public void setName(String name) {   this.name = name;   }   public void setColor(Color color) {   this.color = color;   }      public String getName() {   return this.name;   }   public Color getColor() {   return this.color;   }      }

As you see the getInstance(int i) method allows for the mapping of the number stored in the database to an instance of this class. I am sure there is a better way of pulling these initial value from a properties file but this is only an example.

For the second part you will need a way to allow iBatis to make the conversion. This is where the custom type handler comes into play.

Below is my ColorTypeHandler.java

package dao.ibatis;      import com.ibatis.sqlmap.client.extensions.ParameterSetter;   import com.ibatis.sqlmap.client.extensions.ResultGetter;   import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;   import java.sql.SQLException;   import java.sql.Types;   import Shape.Color;         public class ColorTypeHandler implements TypeHandlerCallback {   public Object getResult(ResultGetter getter) throws SQLException {   int value = getter.getInt();   if (getter.wasNull()) {   return null;   }   Color color = Color.getInstance(value);      return color;   }      public void setParameter(ParameterSetter setter, Object parameter)   throws SQLException {   if (parameter == null) {   setter.setNull(Types.INTEGER);   } else {   Color color = (Color) parameter;   setter.setInt(color.getId());   }   }      public Object valueOf(String s) {   return s;   }   }

You are almost there! All you need left to do is tell iBatis to map instances of Frequency object to this handler.

I added the following line into my SqlMapConfig file.

<typeHandler javaType="domain.Shape$Color" callback="dao.ibatis.ColorTypeHandler"/>
Useful Information

Notice the $ in the class name. This is how Java 1.5 denotes enums. If this were a type safe enum you would just use standard notation.

That is it. No need to change any of your other sqlmap entries. Here is an example of a select and insert that use this handler.

<resultMap class="Shape" id="ShapeResult">   <result column="name" property="name" />   <result column="color" property="color" />   </resultMap>      <select id="getShapeByName" parameterClass="string" resultMap="ShapeResult">   SELECT   *   FROM   SHAPE   WHERE   name = #value#   </select>      <insert id="insertReport" parameterClass="Report">   INSERT INTO   SHAPE (   name,   color   )   values (   #name#,   #color#,   )   </insert>

Notice that there is nothing special that need to be done for the color columns.

Here's a way to cut down on the code.

First, define an interface:

public interface HasValue {            public Integer getValue();      }

Now, when you declare your enum, implement the interface:

public enum Status implements HasValue {      NEED_LOCK { public Integer getValue() { return 90; }},      LOCK_CONTENTION { public Integer getValue() { return 91; }},      FOO { public Integer getValue() { return 100; }}      }

We can now make the TypeHandler shown above more generic:

public abstract class HasValueTypeHandler implements TypeHandlerCallback {            public abstract HasValue[] getEnums();            public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {      if (parameter == null) {      setter.setNull(Types.INTEGER);      } else {      HasValue status = (HasValue) parameter;      setter.setInt(status.getValue());      }      }            public Object getResult(ResultGetter getter) throws SQLException {      if (getter.wasNull())      return null;      int value = getter.getInt();            for (HasValue status : getEnums()) {      if (status.getValue().equals(value)) {      return status;      }      }      throw new UnsupportedOperationException("No such status");      }            public Object valueOf(String s) {      return s;      }      }

Finally, for any enum which implements HasValue, we can now make a TypeHandler as follows:

public class DeploymentStatusTypeHandler extends HasValueTypeHandler {            public HasValue[] getEnums() {      return Deployment.Status.values();      }      }

All that remains is to register this with Ibatis in the sqlMapConfig:

<typeHandler javaType="com.foo.beans.Deployment$Status" callback="com.foo.dao.DeploymentStatusTypeHandler"/>
 Posted by Reuben Firmin at Oct 28, 2005 16:44

Update:

if (getter.wasNull())

threw an exception for me.

Replacing it with:

if (getter.getObject() == null)

works.

 Posted by Reuben Firmin at Oct 28, 2005 17:15

The getResult() method above is broken. When calling getter.getNull before actually getting the column's value (getter.getInt()getNull() will always return true, since no value has been fetched yet. The check for null has to be done after getting the value.

So the correct version of the getResult() from above would look like this:

public Object getResult(ResultGetter getter) throws SQLException {      int value = getter.getInt();            if (getter.wasNull()) {      return null;      }            Color color = Color.getInstance(value);            return color;      }
 Posted by Nils Winkler at Nov 14, 2005 04:33

Top Shape class is wrong, and quite confusing, maybe this way will be cleaner to understand

public class Shape {      public enum Color {      BLUE(1, "0000FF"), RED(2, "FF0000"), GREEN(3, "00FF00");            private int id;      private String rgb;                  private Color(int id, String rgb) {      this.id = id;      this. rgb = rgb;      }            public int getId() {      return this.id;      }            public String getRgb() {      return this.rgb;      }            public static Color getInstance(int i) {      for(Color color : Color.values()) {      if(color.id == i) {      return color;      }      }            throw new IllegalArgumentException("No such Color");      }      }            private String name;      private Color color;                  private Shape(String name, Color color) {      this.color = color;      this.name = name;      }                  public String toString() {      return this.name;      }                  public void setName(String name) {      this.name = name;      }      public void setColor(Color color) {      this.color = color;      }            public String getName() {      return this.name;      }      public Color getColor() {      return this.color;      }            }
 Posted by Levan Dvalishvili at May 30, 2006 21:39

Hey, I just read this and thought that the Usefull Information bar could be confusing to some users. I think that the sentence "Notice the $ in the class name. This is how Java 1.5 denotes enums. If this were a type safe enum you would just use standard notation." is technically incorrect. I'm pretty sure the $ sign is used to denote inner classes. If one were to declare an enum on it's own (in Color.java for example) then the fully qualified name would be domain.Color (assuming you created it in the domain package as there is Shape is just a class.

 Posted by patrick flynn at Aug 14, 2006 16:27

If you just want to store the string value of the Enum in the database, this is what I came up with.

The abstract base class:

public abstract class EnumTypeHandler<E extends Enum> implements TypeHandlerCallback      {      private Class<E> enumClass_;            public EnumTypeHandler(Class<E> enumClass)      {      enumClass_ = enumClass;      }            @SuppressWarnings("unchecked")      public void setParameter(ParameterSetter setter, Object parameter)      throws SQLException      {      setter.setString(((E) parameter).name());      }            public Object getResult(ResultGetter getter) throws SQLException      {      return valueOf(getter.getString());      }            @SuppressWarnings("unchecked")      public Object valueOf(String s)      {      return Enum.valueOf(enumClass_, s);      }      }

Then your enum-specific class:

public class ColorTypeHandler extends EnumTypeHandler<Color>      {      public ColorTypeHandler()      {      super(Color.class);      }      }

Then the enum-specific type handler declaration:

<typeHandler javaType="Color" callback="ColorTypeHandler"/>

Seems to work.

 Posted by Kevin Pauli at Aug 18, 2006 11:32

I have somewhat of the opposite problem. I have a table with multiple columns that need to get mapped to a complex data structure in Java. For example:

public class Circle{      // Assume standard getter/setters for these...      private int raduis;      private Color color;      private Point center;      }

So then my table looks like:

SHAPE{      radiusint,      redint,      greenint,      blueint,      xint,      yint      }

0 0
原创粉丝点击