在学习持久层框架之前,反复编写dao层代码编烦了,就自己实现了一个能方便地根据数据库表创建javaBean,并实现其增删改查,结果集转化为List集合,sql编译语句里参数赋值的自动化的“框架”。
本文所使用的数据库是mysql自带的simple data里的sakila数据库
先看一下使用效果:
- 先编写并运行测试类创建javaBean:
Country,City,Address,Customer
package com.iss.db.dao.toolkit;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import com.iss.db.dao.connectdb.OffLineConn;
import com.iss.db.dao.dbinfo.DBConfig;
/**
* 创建javaBean源文件(下面的代码还可以进一步封装)
*/
public class CreateJavaBeanSource {
@Test
public void test01() throws Exception{
ResultSet rs = TableTool.execQuery(new OffLineConn(new DBConfig()).getConn().prepareStatement("select * from country where 1>2"));
String descTable = DBTool.descTable(rs);
System.out.println(descTable);
DBTool.createClazzToSource(rs, "src/main/java", "com.iss.db.bean.Country", "country");
rs = TableTool.execQuery(new OffLineConn(new DBConfig()).getConn().prepareStatement("select * from city where 1>2"));
descTable = DBTool.descTable(rs);
System.out.println(descTable);
DBTool.createClazzToSource(rs, "src/main/java", "com.iss.db.bean.City", "city");
rs = TableTool.execQuery(new OffLineConn(new DBConfig()).getConn().prepareStatement("select * from address where 1>2"));
descTable = DBTool.descTable(rs);
System.out.println(descTable);
DBTool.createClazzToSource(rs, "src/main/java", "com.iss.db.bean.Address", "address");
rs = TableTool.execQuery(new OffLineConn(new DBConfig()).getConn().prepareStatement("select * from customer where 1>2"));
descTable = DBTool.descTable(rs);
System.out.println(descTable);
DBTool.createClazzToSource(rs, "src/main/java", "com.iss.db.bean.Customer", "customer");
}
}
上述代码中DBTool.createClazzToSource(包含所有列的结果集,工程src名,javaBean全类名,数据库表名),还可以进一步封装使之更易用
- 编写上述4个javaBean对应的DAO类:
- CounryDao.java :
package com.iss.db.dao.tablemanager;
import com.iss.db.bean.Country;
import com.iss.db.dao.tableinfo.TableDic;
public class CountryDao extends TableManager{
public CountryDao(){
super(Country.class,new TableDic("country_id", "country", "country"));
}
}
2.CityDao.java
package com.iss.db.dao.tablemanager;
import com.iss.db.bean.City;
import com.iss.db.dao.tableinfo.TableDic;
public class CityDao extends TableManager {
public CityDao() {
super(City.class, new TableDic("city_id", "city", "city", "country_id"));
}
}
3.AddressDao.java
public class AddressDao extends TableManager{
public AddressDao(){
super(Address.class, new TableDic("address_id", "address", "address","address2","district","city_id","postal_code","phone"));
}
}
4.CustomerDao.java
public class CustomerDao extends TableManager{
public CustomerDao(){
super(Customer.class,new TableDic("customer_id", "customer","store_id","first_name","last_name","email","address_id","active"));
}
}
**上述代码其实只是在给TableManager传入参数,参数提供javaBean的class信息,以及数据库表的主要信息new TableDic(“主键”,”表明”,”更新操作的字段1”,”更新操作的字段2”,……)**
实现过程:
**1. 核心问题:遍历结果集创建java对象很麻烦,各种重复操作,如何省去?****代码组织结构如下:** 1.学过反射,想到可以通过反射调用对象的set方法为之赋值。
2.如过自动赋值,那么对于ResultSet的每一行,如果遍历它的各个字段,查阅发现ResultSet.getMetaData()方法可以获取各个列的信息(名称,类型)。
3.调用javaBean对象的set方法需要考虑传入参数类型, 那么没问题, setmethod.getGenericParameterTypes()[0].getTypeName()可以办到。
4. SQL数据库类型明显要比java里的类型多,需要做个类型对应。
5. 如何自动生成java源文件,那不就是io文件流嘛,拼接好javaBean字符串写入文件即可。
对该功能实现分多个层是为了易于修改和扩展,上图中HeadPicDao.java,MyTool.java与本文介绍的东西无关。
1)包DAO层细分了几个包:
1. dao.connectdb: 获取数据库连接的抽象,可以是永久连接,可以使连接池,OffLineConn是对GetDBConn的一个开发时实现,实际中可以使用数据库连接池,远程连接等来实现。
2. dao.tableinfo: 数据库表关键信息的抽象,关键信息包括(主键,表名,更新操作的字段[]),是创建某个表DAO层唯一需要的输入,TableDic是对TableInfo的实现,也可以用从配置文件获取表信息的方法实现TableInfo。
3. dao.dbinfo: 对数据库信息的抽象,关键信息包括(驱动名,数据库url,用户名,密码),这里就把数据库的信息写死在了DBconfig.java了,如果想换成从配置文件获取,只需要写一个从配置文件获取数据库信息的类实现DBInfo接口即可(策略模式)。
4. dao.toolkit:工具类,获得连接后可以使用的工具DBTool,制定了表之后可用的工具TableTool,数据格式转化工具MyTool(实现json, javaBean, ResultSet 等值间的转化)
5. dao.tablemanager: 每个表Dao的模板类,以及每个数据表对应的继承类,在继承里指定对应的javaBean和tableinfo信息即可
2) 特点:
1. 根据数据库表自动创建javaBean文件。
2. ResultSet到javaBean的变化依据数据库字段名在javaBean中找对应的set方法,判断set方法的输入参数类型和数据库表类型是否一致(类型匹配),匹配则调用这个set方法进行赋值。
3. 更新类操作根据初始化时指定的数据库表信息里的主键信息、更新字段信息进行,程序根据这些字段名自动从javaBean中提取信息进行更新或者插入操作。
4. 查询自动包装成javaBean,根据的是数据库表的属性。
5. 综上,javaBean可以任意添加成员变量,其它方法,只需要满足javaBean里的get方法是数据库属性的超集、javaBean里的set方法是tableinfo指定更新属性的超集即可。
核心代码如下 ( 其余代码会在后面贴上 )
**1. 结果集转化为javaBean集合(MyTool.java里面的部分代码)**数据类型映射表:
// 类型对照表,试着用反射实现javaBean装箱操作
public final static Map<String, String> TYPE_DIC = new HashMap<String, String>() {
{
put("VARCHAR", "java.lang.String");
put("VARCHAR2", "java.lang.String");
put("CHAR", "java.lang.String");
put("TEXT", "java.lang.String");
put("MEDIUMTEXT", "java.lang.String");
put("LONGTEXT", "java.lang.String");
put("INT", "java.lang.Integer,int");
put("SMALLINT", "java.lang.Short,short");
put("TINYINT", "java.lang.Short,short,java.lang.Boolean");
put("SMALLINT UNSIGNED","java.lang.Short,short");
put("FLOAT", "java.lang.Float,float");
put("DECIMAL", "java.lang.Float,folat");
put("DOUBLE", "java.lang.Double,double");
put("YEAR", "java.sql.Date,java.util.Date");
put("DATE", "java.sql.Date,java.util.Date");
put("TIME", "java.sql.Date,java.util.Date");
put("DATETIME", "java.sql.Timestamp,java.util.Date");
put("TIMESTAMP", "java.sql.Timestamp,java.util.Date");
put("ID", "java.lang.Long,long");
put("BIT", "java.lang.Boolean,boolean");
put("BINARY", "java.lang.Boolean,boolean");
put("BOOLEAN", "java.lang.Boolean,boolean");
put("BIGINT", "java.math.BigInteger,long");
put("INTEGER", "java.lang.Integer,int");// 或者long ?
put("BLOB", "java.lang.String");// 或者 java.lang.byte[] ?
put("GEOMETRY","java.lang.String");
put("TINYINT UNSIGNED","java.lang.Integer,int,java.lang.Byte,buty");
}
};
// 可以通过 valueOf(String arg)装箱的基本数据类型
public final static Map<String, String> BASIC_TYPE = new HashMap<String, String>() {
{
put(