ResultSet结果集转换为实体对象实现方案

本文介绍了一种优化方法,用于将数据库查询结果集(ResultSet)高效地转换为实体对象(Entity)。该方法通过封装ResultSet到实体对象的过程,减少了手动编写大量get和set方法的工作量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ResultSet结果集转换为实体对象实现方案

在应用开发中,我们从数据库查询出的结果集(ResultSet)一般都需要取得(get)其中的数据然后存放到(set)实体对象(Entity,有的称为VO值对象或称为PO持久化对象)中,以便进一步的处理需要。常用也最容易理解的方式就是从ResultSetget相应的字段值后调用实体对象的set方法,把值保存在实体对象中。这种方式编码量很大,特别是很多字段的查询,编码就会觉得很烦琐。这里我们提供了一种方法,封装实现了ResultSet结果集转换为实体对象(Entity,或叫VO值对象,或PO持久化对象)的方法。

注:三流合一项目有种实现方式,不过我觉得有点烦琐,这里做了重新设计定义,欢迎各位大家多多指教交流。

 

 

 

 

一、定义结果集返回表结构对象

首先我们需要对返回的结果集进行分析,需要知道它返回了那些字段,字段名、字段类型是什么等,结果集表结构对象定义如下:

public class DataTableEntity

{

  //查询出的ReslutSet中的字段数量

  private int columnCount = 0;

  //字段名称数组

  private String[] columnNames;

  //字段类型数组

  private int[] columnTypes;

 

 

 

 

  //默认构造器

  public DataTableEntity()

  {

    this(0);

  }

 

 

 

 

  //初始化构造器

  public DataTableEntity(int columnCount)

  {

    this.columnCount = columnCount;

    this.columnNames = new String[columnCount];

    this.columnTypes = new int[columnCount];

  }

 

 

 

 

  //获取字段数量

  public int getColumnCount()

  {

    return this.columnCount;

  }

 

 

 

 

  //获取字段名称数组

  public String[] getColumnNames()

  {

    return this.columnNames;

  }

 

 

 

 

  //获取第index个字段名称,如果index字段不存在,则抛出ArrayIndexOutOfBoundsException异常

  public String getColumnName(int index)

  {

    if (index <= this.columnCount)

    {

      return this.columnNames[index];

    }

    else

    {

      throw new ArrayIndexOutOfBoundsException();

    }

  }

 

 

 

 

  //设置字段名称数组

  public void setColumnNames(String[] columnNames)

  {

    this.columnNames = columnNames;

  }

 

  //设置第index个字段名称,如果index字段不存在,则抛出ArrayIndexOutOfBoundsException异常

  public void setColumnName(String columnName, int index)

  {

    if(index <= this.columnCount)

    {

      this.columnNames[index] = columnName;

    }

    else

    {

      throw new ArrayIndexOutOfBoundsException();

    }

  }

 

  //获取字段类型数组

  public int[] getColumnTypes()

  {

    return this.columnTypes;

  }

 

  //获取字段类型

  public int getColumnType(int index)

  {

    if(index <= this.columnCount)

    {

      return this.columnTypes[index];

    }

    else

    {

      throw new ArrayIndexOutOfBoundsException();

    }

  }

  //设置字段类型数组

  public void setColumnTypes(int[] columnTypes)

  {

    this.columnTypes = columnTypes;

  }

  //获取字段类型

  public void setColumnType(int columnType, int index)

  {

    if(index <= this.columnCount)

    {

      this.columnTypes[index] = columnType;

    }

    else

    {

      throw new ArrayIndexOutOfBoundsException();

    }

  }

}

 

 

 

 

二、定义实体对象(或值对象)的方法实体对象

我们在实体对象中定义setget等方法,当ResultSet转换为Entity时,我们需要调用Entity的一系列set方法,那么我们就需要知道在entity中都定义了哪些方法,这些方法的参数是什么?返回值是什么?以及抛出的异常等信息。

因此,我们需要对实体对象进行分析,定义一个对象来存储分析后的实体对象中定义的方法(Methods)信息。

基于以上需要,我们定义了一个方法实体对象:

import java.util.ArrayList;

 

 

 

 

public class MethodEntity

{

    //方法名称

    private String methodName;

    //重载方法个数

    private int repeatMethodNum = 1;

    //方法参数类型列表   

    private Class[] methodParamTypes;

    //存放重载方法参数

    private ArrayList repeatMethodsParamTypes;

   

    /**获取参数名称

     * @return

     */

    public String getMethodName()

    {

        return methodName;

    }

    /**获取方法参数类型列表

     * @return

     */

    public Class[] getMethodParamTypes()

    {

        return methodParamTypes;

    }

    /**设置参数名称

     * @param string

     */

    public void setMethodName(String string)

    {

        methodName = string;

    }

    /**设置参数类型列表

     * @param classes

     */

    public void setMethodParamTypes(Class[] classes)

    {

        methodParamTypes = classes;

    }

    /**获取重载方法个数

     * @return

     */

    public int getRepeatMethodNum()

    {

        return repeatMethodNum;

    }

    /**获取第i个重载方法参数列表

     * @return

     */

    public Class[] getRepeatMethodsParamTypes(int i)

    {

        int count = this.repeatMethodsParamTypes.size();

        if(i <= count)

        {

            return (Class[])this.repeatMethodsParamTypes.get(i);

        }

        else

        {

          throw new ArrayIndexOutOfBoundsException();

        }

    }

    /**设置重载方法个数

     * @param i

     */

    public void setRepeatMethodNum(int i)

    {

        repeatMethodNum = i;

    }

    /**设置重载方法参数类型

     * @param list

     */

    public void setRepeatMethodsParamTypes(ArrayList list)

    {

        repeatMethodsParamTypes = list;

    }

    /**获取重载方法类型

     * @return

     */

    public ArrayList getRepeatMethodsParamTypes()

    {

        return repeatMethodsParamTypes;

    }

   

/**

     * 设置重载方法参数类型列表

     * @param paramTypes

     */ 

public void setRepeatMethodsParamTypes(Class[] paramTypes)

    {

        if(this.repeatMethodsParamTypes == null)

            this.repeatMethodsParamTypes = new ArrayList();

           

        repeatMethodsParamTypes.add(paramTypes);

    }

}

 

 

 

 

三、实现ResultSetEntity对象转换方法定义

定义完毕方法实体对象和结果集表结构实体对象后,我们就可以对返回的结果集进行分析,把分析结构分别存储在方法实体对象和结果集表结构实体对象中。

首先我们需要知道返回的结果集和哪个实体对象相对应,需要指定ResultSet转化为的Entity对象类名称。

然后我们对这个实体对象类中定义的方法进行分析,获取实体中定义的方法名称,方法参数类型等信息,如下:

//注册实体, strEntity指定的实体类名称字符串

      Class classEntity = Class.forName(strEntity);

      //获取实体中定义的方法

      HashMap hmMethods = new HashMap();

      for (int i = 0; i < classEntity.getDeclaredMethods().length; i++)

      {

          MethodEntity methodEntity = new MethodEntity();

          //方法的名称

          String methodName = classEntity.getDeclaredMethods()[i].getName();

          String methodKey = methodName.toUpperCase();

          //方法的参数

          Class[] paramTypes = classEntity.getDeclaredMethods()[i].getParameterTypes();

         

          methodEntity.setMethodName(methodName);

          methodEntity.setMethodParamTypes(paramTypes);

         

          //处理方法重载

          if(hmMethods.containsKey(methodKey))

          {

              methodEntity.setRepeatMethodNum(methodEntity.getRepeatMethodNum()+1);

              methodEntity.setRepeatMethodsParamTypes(paramTypes);

          }

          else

          {

            hmMethods.put(methodKey, methodEntity);

          }

}

这里我们把解析出来的方法信息存储在方法实体对象中,通过全大写方法名作为key存放在HashMap中。

这个方法的实现,我们支持方法的重载。

然后我们分析返回结果集的字段信息,存放在表结构对象实体中:

ResultSetMetaData rsMetaData = rsResult.getMetaData();

        int columnCount = rsMetaData.getColumnCount();

        dataTable = new DataTableEntity(columnCount);

        //获取字段名称,类型

        for (int i = 0; i < columnCount; i++)

        {

          String columnName = rsMetaData.getColumnName(i + 1);

          int columnType = rsMetaData.getColumnType(i + 1);

         

          dataTable.setColumnName(columnName, i);

          dataTable.setColumnType(columnType, i);

    }

下面就可以对返回的结果集数据进行处理了:

while(rsResult.next())

      {

        //调用方法,根据字段名在hsMethods中查找对应的set方法

}

完整是实现如下:

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.lang.reflect.Method;

import java.util.HashMap;

import java.lang.reflect.Array;

 

 

 

 

public class ConvertResultSetToEntity

{

 

  /**

   * 实现结果集到实体对象/值对象/持久化对象转换

   * @param rsResult ResultSet

   * @param strEntity String

   * @throws Exception

   * @return Object[]

   */

  public static Object[] parseDataEntityBeans(ResultSet rsResult, String strEntity)

      throws Exception

  {

      DataTableEntity dataTable = null;

      java.util.List listResult = new java.util.ArrayList();

 

 

 

 

      //注册实体,strEntity指定的实体类名称字符串

      Class classEntity = Class.forName(strEntity);

      //获取实体中定义的方法

      HashMap hmMethods = new HashMap();

      for (int i = 0; i < classEntity.getDeclaredMethods().length; i++)

      {

          MethodEntity methodEntity = new MethodEntity();

          //方法的名称

          String methodName = classEntity.getDeclaredMethods()[i].getName();

          String methodKey = methodName.toUpperCase();

          //方法的参数

          Class[] paramTypes = classEntity.getDeclaredMethods()[i].getParameterTypes();

         

          methodEntity.setMethodName(methodName);

          methodEntity.setMethodParamTypes(paramTypes);

         

          //处理方法重载

          if(hmMethods.containsKey(methodKey))

          {

              methodEntity.setRepeatMethodNum(methodEntity.getRepeatMethodNum()+1);

              methodEntity.setRepeatMethodsParamTypes(paramTypes);

          }

          else

          {

            hmMethods.put(methodKey, methodEntity);

          }

      }

     

      //处理ResultSet结构体信息

      if(rsResult != null)

      {

        ResultSetMetaData rsMetaData = rsResult.getMetaData();

        int columnCount = rsMetaData.getColumnCount();

        dataTable = new DataTableEntity(columnCount);

        //获取字段名称,类型

        for (int i = 0; i < columnCount; i++)

        {

          String columnName = rsMetaData.getColumnName(i + 1);

          int columnType = rsMetaData.getColumnType(i + 1);

         

          dataTable.setColumnName(columnName, i);

          dataTable.setColumnType(columnType, i);

        }

      }

     

      //处理ResultSet数据信息

      while(rsResult.next())

      {

        //调用方法,根据字段名在hsMethods中查找对应的set方法

        Object objResult = ParseObjectFromResultSet(rsResult, dataTable, classEntity, hmMethods);

        listResult.add(objResult);

      }

     

      //以数组方式返回

      Object objResutlArray = Array.newInstance(classEntity, listResult.size());

      listResult.toArray((Object[]) objResutlArray);

 

 

 

 

      return (Object[])objResutlArray;

  }

 

 /**

  * Resultset中解析出单行记录对象,存储在实体对象中

  */

  public static Object ParseObjectFromResultSet(

      ResultSet rs,

      DataTableEntity dataTable,

      Class classEntity,

      java.util.HashMap hsMethods)

      throws Exception

  {

      Object objEntity = classEntity.newInstance();

      Method method = null;

     

      int nColumnCount = dataTable.getColumnCount();

      String[] strColumnNames = dataTable.getColumnNames();

     

      for (int i = 0; i < nColumnCount; i++)

      {

          //获取字段值

          Object objColumnValue = rs.getObject(strColumnNames[i]);

 

 

 

 

          //HashMap中的方法名key

          String strMethodKey = null;

         

          //获取set方法名

          if (strColumnNames[i] != null)

          {

              strMethodKey = String.valueOf("SET" + strColumnNames[i].toUpperCase());

          }

          //值和方法都不为空,这里方法名不为空即可,值可以为空的

          if (strMethodKey!=  null)

          {

              //判断字段的类型,方法名,参数类型

             try

             {  

                MethodEntity methodEntity = (MethodEntity)hsMethods.get(strMethodKey);

               

                String methodName = methodEntity.getMethodName();

                int repeatMethodNum = methodEntity.getRepeatMethodNum();

               

                Class[] paramTypes = methodEntity.getMethodParamTypes();

                method = classEntity.getMethod(methodName, paramTypes);

               

                //如果重载方法数 > 1,则判断是否有java.lang.IllegalArgumentException异常,循环处理

                try

                {

                    //设置参数,实体对象,实体对象方法参数

                    method.invoke(objEntity, new Object[]{objColumnValue});

                }

                catch(java.lang.IllegalArgumentException e)

                {

                    //处理重载方法

                    for(int j = 1; j < repeatMethodNum; j++)

                    {

                         try

                         {

                            Class[] repeatParamTypes = methodEntity.getRepeatMethodsParamTypes(j - 1);

                            method = classEntity.getMethod(methodName, repeatParamTypes);

                            method.invoke(objEntity, new Object[]{objColumnValue});

                            break;

                        }

                        catch(java.lang.IllegalArgumentException ex)

                        {

                            continue;

                         }

                    }

                }

             }

             catch (NoSuchMethodException e)

             {

               throw new NoSuchMethodException();

             }

             catch(Exception ex)

             {

               ex.printStackTrace();

             }

          }

      }

      return objEntity;

  }

}

<think>好的,我现在要解决的问题是,实体类中的Date类型字段在使用MyBatis查询时,返回的结果是时间戳格式,而不是预期的日期格式。我需要找出原因,并给出解决方案。 首先,我需要回忆一下MyBatis如何处理数据中的日期类型和Java实体类之间的映射。通常,数据中的日期时间类型(如DATE、DATETIME、TIMESTAMP)会被JDBC驱动转换为Java的java.util.Date或java.sql.Date、Time、Timestamp等类型。如果实体类中使用的是java.util.Date,而数据中的字段是DATETIME或TIMESTAMP,MyBatis应该能正确映射,但显示为时间戳可能是因为Date对象的toString()方法默认输出格式的问题。 接下来,用户提到返回的是时间戳格式,这可能是指从数据查询得到的日期字段在Java中被转换为长整型时间戳,或者在使用时被格式化为时间戳字符串。需要区分这两种情况。 情况一:数据中的字段是时间戳类型(如BIGINT存储的毫秒数),而实体类中使用Date类型。这时,MyBatis无法自动转换,需要手动处理类型转换。 情况二:数据中的字段是日期时间类型(如DATETIME),但查询结果在Java中显示为时间戳格式,这可能是因为Date对象在输出时(如日志、JSON序列化)使用了默认的toString()方法,导致显示为类似"Wed Dec 20 10:55:03 CST 2023"的格式,或者转换为时间戳数值。 需要确认具体是哪一种情况。假设用户的情况是数据字段存储的是日期时间类型,但返回的Date对象转换成字符串时显示为时间戳格式。这时候的问题可能出在数据的序列化过程,比如在转换成JSON响应时,没有正确格式化日期字段。 解决方案可能包括: 1. 在MyBatis的ResultMap中指定字段的类型处理器(TypeHandler),或者使用注解指定jdbcType和javaType。 2. 在实体类的Date字段上使用注解,如@JsonFormat(如果使用Jackson进行JSON序列化)来指定日期格式。 3. 配置全局的日期序列化格式,例如在Spring Boot中配置Jackson的日期格式。 另外,需要检查数据驱动是否正确处理日期类型。例如,MySQL的JDBC驱动在连接参数中可以通过指定useLegacyDatetimeCode=false和serverTimezone来正确处理时区问题,避免日期转换错误。 可能的步骤: 1. 确认数据字段类型是否为日期时间类型,如DATETIME或TIMESTAMP,而不是存储时间戳的数值类型。 2. 检查实体类中的字段是否为java.util.Date或java.sql.Timestamp,如果数据字段是TIMESTAMP,MyBatis默认会映射为Timestamp,而DATE会映射为java.sql.Date。但java.util.Date可以接收这些类型。 3. 如果数据存储的是时间戳数值,则需要自定义TypeHandler来转换数值为Date对象。 例如,如果数据中的字段是BIGINT类型存储时间戳,那么在MyBatis的查询结果映射时,可以使用自定义的TypeHandler将Long转换为Date。 4. 如果问题出现在JSON序列化中,例如使用Spring MVC返回JSON响应,Date对象会被Jackson序列化为时间戳。此时需要在字段上添加@JsonFormat注解,指定日期格式。 例如: @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createDate; 5. 另外,可以在application.properties中配置全局的日期格式: spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 这样所有Date类型的字段都会按此格式序列化。 总结可能的原因和解决方法: - 数据字段类型正确,但MyBatis处理无误,问题出在后续的序列化格式,需配置日期格式化。 - 数据字段实际存储的是时间戳数值,需要自定义TypeHandler进行转换。 需要用户进一步确认数据字段的类型以及问题出现的场景(如直接输出对象还是JSON响应)。根据常见情况,可能更倾向于JSON序列化的问题,因此建议检查并配置日期格式的序列化方式。</think>### 问题原因分析: 当实体类中的`java.util.Date`字段从MyBatis查询结果中映射时,若返回时间戳格式(如`1630320000000`或`Wed Dec 20 10:55:03 CST 2023`),通常由以下原因导致: 1. **数据字段类型不匹配**:数据存储的是数值型时间戳(如`BIGINT`),而非日期类型(如`DATETIME`)。 2. **序列化格式问题**:MyBatis正确映射为`Date`对象,但后续序列化(如JSON转换)未指定格式,导致显示为时间戳。 --- ### 解决方案: #### 情况1:数据字段是数值型时间戳(如`BIGINT`) 若数据字段直接存储时间戳数值(如`1630320000000`),需通过**自定义TypeHandler**将数值转为`Date`。 **步骤:** 1. **创建TypeHandler类**: ```java @MappedTypes(Date.class) @MappedJdbcTypes(JdbcType.BIGINT) public class TimestampToDateTypeHandler extends BaseTypeHandler<Date> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException { ps.setLong(i, parameter.getTime()); } @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { long timestamp = rs.getLong(columnName); return rs.wasNull() ? null : new Date(timestamp); } @Override public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException { long timestamp = rs.getLong(columnIndex); return rs.wasNull() ? null : new Date(timestamp); } @Override public Date getNullableResult(SQLSession session, Object parameter) throws SQLException { // 根据实际情况实现(如CallableStatement) return null; } } ``` 2. **在MyBatis配置中注册TypeHandler**: ```xml <!-- mybatis-config.xml --> <typeHandlers> <typeHandler handler="com.example.TimestampToDateTypeHandler"/> </typeHandlers> ``` 3. **在Mapper XML中指定字段类型处理器**: ```xml <resultMap id="userResultMap" type="User"> <result column="create_time" property="createTime" typeHandler="com.example.TimestampToDateTypeHandler"/> </resultMap> ``` --- #### 情况2:数据字段是日期类型(如`DATETIME`),但序列化为时间戳 若MyBatis正确返回`Date`对象,但JSON序列化显示时间戳,需**指定日期格式**。 **方法1:使用`@JsonFormat`注解(推荐)** ```java public class User { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; } ``` **方法2:全局配置Jackson序列化格式(Spring Boot)** ```properties # application.properties spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 ``` --- #### 补充检查点: 1. **确认数据字段类型**: - 使用`DESC table_name;`检查字段是否为`DATE`/`DATETIME`/`TIMESTAMP`。 2. **验证JDBC连接参数**(如MySQL): ```properties spring.datasource.url=jdbc:mysql://localhost:3306/db?useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai ``` - 避免因时区或旧版驱动导致日期解析错误。 --- ### 总结: - **数据存数值时间戳** → 自定义TypeHandler。 - **数据存日期类型** → 配置序列化格式(`@JsonFormat`或全局配置)。 - 确保JDBC驱动正确处理时区。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值