代码改变世界

JDBC之运用反射模拟ORM

2009-10-23 16:47  BlueDream  阅读(2075)  评论(0编辑  收藏  举报

上一篇我们使用了结果集元数据实现了将结果集封装到List<Map<K,V>>中返回.

ORM:对象-关系数据库映射(Object/Relation Mapping).以简单的方式提供了领域对象模型与数据库间的映射.典型的Hibernate,iBatis

这里仅是练习.功能还很弱.

程序目的:传入sql语句返回字段如果和传入对象模型的setXX方法对应则自动赋值.并返回该对象.

首先构建User模型.应该和数据库字段相对应.数据库结构如下:

User模型:

 

package com.test.reflection;

public class User {
    
private Integer id;
    
private String firstname;
    
private String lastname;
    
private Integer age;

    
// 约定的默认构造器(必须)
    public User() {

    }

    @Override
    
public String toString() {
        
return "id: " + this.id + " firstname: " + this.firstname
                
+ " lastname: " + this.lastname + " age: " + this.age;
    }

    
public Integer getId() {
        
return id;
    }

    
public void setId(Integer id) {
        
this.id = id;
    }

    
public String getFirstname() {
        
return firstname;
    }

    
public void setFirstname(String firstname) {
        
this.firstname = firstname;
    }

    
public String getLastname() {
        
return lastname;
    }

    
public void setLastname(String lastname) {
        
this.lastname = lastname;
    }

    
public Integer getAge() {
        
return age;
    }

    
public void setAge(Integer age) {
        
this.age = age;
    }

}

然后是上次提供的DBUtils:

package com.test.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public final class DBUtils {
    
private static String url = "jdbc:mysql://localhost:3306/mytest";
    
private static String user = "root";
    
private static String password = "root";

    
// 获得连接
    public static Connection getConn() throws SQLException {
        
return DriverManager.getConnection(url, user, password);
    }

    
// 释放连接
    public static void free(ResultSet rs, PreparedStatement ps, Connection conn) {
        
try {
            
if (rs != null) {
                rs.close();
            }
        } 
catch (SQLException e) {
            e.printStackTrace();
        } 
finally {
            
try {
                
if (ps != null) {
                    ps.close();
                }
            } 
catch (SQLException e) {
                e.printStackTrace();
            } 
finally {

                
try {
                    
if (conn != null) {
                        conn.close();
                    }
                } 
catch (SQLException e) {
                    e.printStackTrace();
                }

            }

        }

    }

    
// 加载驱动
    static {
        
try {
            Class.forName(
"com.mysql.jdbc.Driver");
        } 
catch (ClassNotFoundException e) {
            System.out.println(
"驱动加载出错");
        }
    }

}

最后最主要的ORMExample:

package com.test.reflection;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

import com.test.jdbc.DBUtils;

public class ORMExample {

    
public static void main(String[] args) {
        User user 
= (User) getObject(
                
"select id, firstname, lastname, age from users where id = 1",
                User.
class);
        System.out.println(user);

    }

    
public static <T>T getObject(String sql, Class<T> clazz) {
        Connection conn 
= null;
        PreparedStatement ps 
= null;
        ResultSet rs 
= null;

        
try {
            conn 
= DBUtils.getConn();
            ps 
= conn.prepareStatement(sql);
            rs 
= ps.executeQuery();
            
// 必须有无参的构造函数
            T obj = null;
            
// 获得共有方法
            Method[] ms = clazz.getMethods();
            
// 通过结果集元数据获得列数
            ResultSetMetaData rsmd = rs.getMetaData();
            
int columnCount = rsmd.getColumnCount();
            
            
if (rs.next()) {
                obj 
= clazz.newInstance();
                
for (int i = 1; i <= columnCount; i++) {
                    String colName 
= rsmd.getColumnLabel(i);
                    String methodName 
= "set"
                            
+ colName.substring(01).toUpperCase()
                            
+ colName.substring(1);
                    
// 循环读取所有方法
                    for (Method m : ms) {
                        
// 列名和set方法名如果相同则调用该方法
                        if (methodName.equals(m.getName())) {
                            m.invoke(obj, rs.getObject(colName));
                        }
                    }
                }
            }
            
return obj;
        } 
catch (Exception e) {
            
throw new RuntimeException();
        } 
finally {
            DBUtils.free(rs, ps, conn);
        }
    }
}

数据库值如下:

代码运行结果为:

id: 1 firstname: 咖 lastname: 咖 age: 23

具体的方法可见代码注释.通过反射获取方法和元数据的列比较相同的便动态执行.

下篇将要介绍自己写连接池.待续.