先上代码
package test;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.junit.Test;
import util.DbHelper;
/**
* @ClassName: DataBaseMetaDateTest
* @Description: TODO(这里用一句话描述这个类的作用)
* @author A18ccms a18ccms_gmail_com
* @date 2016-11-28 下午4:29:26
*
*/
public class DataBaseMetaDateTest {
/**
* @Title: main
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param args 设定文件
* @return void 返回类型
* @throws
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
@Test
public void testParameter() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DbHelper.getConnection();
String sql = "select * from wangcc_user where id=1";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
String[] columnNames = new String[count];
for (int i = 1; i <= count; i++) {
// 如果调用方法 ResultSet.getObject 从列中获取值,则返回构造其实例的 Java
// 类的完全限定名称。ResultSet.getObject 可能返回此方法所返回的类的子类。
// java.math.BigDecimal
System.out.println(rsmd.getColumnClassName(i));
// 获取用于打印输出和显示的指定列的建议标题。 建议标题通常由 SQL AS 子句来指定。也就是获取别名
// 如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同
System.out.println(rsmd.getColumnLabel(i));
// 获取指定列的 SQL 类型。
System.out.println(rsmd.getColumnType(i));
System.out.println(rsmd.getColumnTypeName(i));
System.out.println(rsmd.getColumnName(i));
columnNames[i - 1] = rsmd.getColumnName(i);
}
} catch (SQLException e) {
// TODO: handle exception
} finally {
DbHelper.free(rs, ps, conn);
}
}
/**
* @Title: test
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param 设定文件
* @return void 返回类型
* @throws 这些信息对于我们使用人员来说可能没有太大的用处
* ,但是对于开发框架的人来说用处很大的,比如Hibernate框架,他要做到兼容所有的数据库特性的话,
* 必须要将不同特性统一起来
* ,所以他肯定要获取数据库的元数据信息的,后面会说到Hibernate中有一个配置叫做:方言,这个就是用来设置数据库名称的。
*/
@Test
public void test() {
Connection conn = null;
try {
conn = DbHelper.getConnection();
DatabaseMetaData metaData = conn.getMetaData();
System.out.println("Database name:"
+ metaData.getDatabaseProductName());
System.out.println("driverName:" + metaData.getDriverName());
System.out.println("isSupportBatch:"
+ metaData.supportsBatchUpdates());
System.out.println("isSupportTranscational:"
+ metaData.supportsTransactions());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DbHelper.free(null, null, conn);
}
}
}
上面的testParameter()方法,通过利用ResultSetMetaData接口的实现类的方法得到数据库列的信息。下面引用一段API中的话:
ResultSetMetaData:可用于获取关于 ResultSet 对象中列的类型和属性信息的对象。以下代码片段创建 ResultSet 对象 rs,创建 ResultSetMetaData 对象 rsmd,并使用 rsmd 查找 rs 有多少列,以及 rs 中的第一列是否可以在 WHERE 子句中使用。
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
ResultSetMetaData rsmd = rs.getMetaData();
int numberOfColumns = rsmd.getColumnCount();
boolean b = rsmd.isSearchable(1);
我们可以通过该接口很方便的得到数据库列名以及他的别名等一系列信息,这样我们就有了构建ORM框架的最基本的基础。
我们知道,所有的javabean (实体类)都有属性相对应的get和set方法.
而且属性与get set方法的对应是有规则限制的 ,例如 property 为name时,get方法必须为getName();get后接的是属性的首字母大写对应的字符串。get方法第一个字母必须是大写,但是不能连续接两个大写字母。
当我们了解了有这样的对应关系之后,再联系我们在学习java基础时一再强调的反射技术。(学好反射和动态代理以及注解是学好框架的基础)
我们便可以得出一个基本的ORM框架雏形。
public static <T> T test(String sql, Class<T> clazz) {
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
conn = DbHelper.getConnection();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
String[] columnNames = new String[count];
for (int i = 0; i < count; i++) {
// 获取用于打印输出和显示的指定列的建议标题。建议标题通常由 SQL AS 子句来指定。也就是获取别名
// 如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同。
columnNames[i] = rsmd.getColumnLabel(i + 1);
}
T object = clazz.newInstance();
if (rs.next()) {
Method[] methods = object.getClass().getMethods();
for (int i = 0; i < columnNames.length; i++) {
String methodName = "set" + columnNames[i];
for (Method method : methods) {
if (methodName.equalsIgnoreCase(method.getName())) {
method.invoke(object, rs.getObject(columnNames[i]));
}
}
}
}
return object;
} catch (Exception e) {
// TODO: handle exception
} finally {
DbHelper.free(rs, ps, conn);
}
return null;
}
为了使数据库列名与属性名相同,我们可以使用设置别名的方式来控制数据一致。
接下来,我们来编写一个自己的简单ORM框架。
package strategy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import util.DbHelper;
public abstract class AbstractDao {
// 增删改都可以
protected int update(String sql, Object[] args) {
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
int count = 0;
try {
conn = DbHelper.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
count = ps.executeUpdate();
System.out.println("更新行数:" + count);
} catch (SQLException e) {
// TODO: handle exception
} finally {
DbHelper.free(rs, ps, conn);
}
return count;
}
protected Object find(String sql, Object[] args, RowMapper rowmapper) {
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
Object obj = null;
try {
conn = DbHelper.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
obj = rowmapper.mapRow(rs);
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
}
package strategy;
import java.sql.ResultSet;
import java.sql.SQLException;
public interface RowMapper {
public Object mapRow(ResultSet rs) throws SQLException;
}
package strategy;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDao extends AbstractDao {
public int update(String sql, Object[] args) {
return super.update(sql, args);
}
// 策略模式。
public String findusername(String sql, int id) {
Object[] args = { id };
String name = (String) super.find(sql, args, new RowMapper() {
@Override
public Object mapRow(ResultSet rs) throws SQLException {
// TODO Auto-generated method stub
return rs.getObject("name");
}
});
return name;
}
}
这段代码我们明天再讲解。