现在要解决的问题,是获得一个未知类型对象的类名,调用该类未知的方法,还要取得其未知的属性,只要这个类已经配置好在Xml文件中。
下面定义了一个ObjectSession接口,方法包括插入、查询、更新三个基本功能。
ObjectSession
public interface ObjectSession {
/**
* 保存这个对象的各个属性值到db,
* 类名和数据库表名对象,对象属性名和表列名对应
* @param o 要保存的对象
* @return 存储成功返回 true,失败返回 false
* @throws Exception
*/
public boolean saveObject(Object o) throws Exception;
/**
* 从数据库中读取一个指定类的指定主键ID的记录
* @param id,要读取的记录的主键
* @param c,要读取的记录的类型
* @throws Exception
*/
public Object getObject(int id,Class c) throws Exception;
/**
*
* @param temp要更新的对象
* @return成功返回 true,失败返回 false
* @throws Exception
*/
public boolean updateObject(Object temp) throws Exception;
}
从SaveObject开始来一一实现它
ObjectDao
public class ObjectDao implements ObjectSession{
public boolean saveObject(Object o) throws NoneClassInXmlException{
Class c=o.getClass();
String classname=c.getName();
String xmlUrl="pojo/"+classname.substring(classname.lastIndexOf(".")+1)+".hbm.xml";
if(!Connect2Database.getResources().contains(xmlUrl)){
throw new NoneClassInXmlException();
}
这是确认要操作的类是否配置在hibernate.cfg.xml文件中,如果没有则抛出NoneClassInXmlException异常
NoneClassInXmlException
public class NoneClassInXmlException extends Exception{
public NoneClassInXmlException(){
super("配置文件中不存在该类");
}
}
如果存在配置文件,那么读取配置文件
PojoXml xml=new PojoXml();
new PojoXmlReader(xml,"main/src/"+xmlUrl).read();
编写sql语句,要插入一条记录sql语句的格式是
insert into tablename (column 1,......,column n) value (?,......?)
要知道表的列名很简单,可以从Xml中取到
String sql="insert into";
sql+=" "+xml.getTablename();
/** 设置要插入的参数列 **/
sql+=" (";
List<String> columnList=xml.getColumnList();
for(int i=xml.getKey_auto();i<columnList.size();i++){
sql+=columnList.get(i)+",";
}
sql=sql.substring(0, sql.length()-1);
sql+=")";
如果key_auto的值为1,那主键是自增长的,在插入时则不需要主键的信息,如果不是自增长的那么列应该从主键开始。因为主键的属性名和列名都是最先放入队列的。
接下来要设置插入的值
如果要插入的属性名是param,那么对应的get方法就是getParam
sql+=" values(";
List<String> nameList=xml.getColumnList();
for(int i=xml.getKey_auto();i<nameList.size();i++){
String paramName=nameList.get(i);
String methodName="get"+paramName.substring(0, 1).toUpperCase()+paramName.substring(1);
注意get之后的字母是大写
get方法是public 可以直接取到
getMethod和getDeclaredMethod,前者是取得类中的public方法包括从父类中继承的,后者是取得在类中声明的方法包括private。
Method method;
try {
method = c.getMethod(methodName, null);
getMethod方法的第一参数是方法名,第二个参数是方法的参数列表数组,get方法是没有参数的,所以为null
String value;
value = method.invoke(o, null).toString();
invoke方法的第一参数是唤醒该方法的对象,第二个参数是参数列表数组,这里为null
value = method.invoke(o, null).toString();
sql+="'"+value+"',";
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
sql=sql.substring(0, sql.length()-1);
sql+=")";
这样sql语句就完成了,执行sql语句就完成了
/** 创建数据库连接对象 执行sql语句 **/
Connection connection=Connect2Database.getConnection();
try {
Statement stm=connection.createStatement();
stm.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
return false;
}
return true;
返回true,成功
更新sql语句跟插入差不多,同样要实现的代码也差不多,就不累赘了
下面是如何读取一条记录
public Object getObject(int id, Class c) throws NoneClassInXmlException {
同样要判断该类是否已配置
/** 如果配置文件中不存在此类 **/
if(!Connect2Database.getResources().contains(xmlUrl)){
throw new NoneClassInXmlException();
}
查询的sql语句格式为 select * from tablename where key=?
要生成语句很简单,但重点在将数据读出来
String sql="select * from "+xml.getTablename()+" where "+xml.getKey()+" = "+id;
创建数据库连接对象,执行sql语句
Connection connection=Connect2Database.getConnection();
try {
Statement stm=connection.createStatement();
/** 执行sql 查询记录 **/
ResultSet rs=stm.executeQuery(sql);
if(rs.next()){
Object obj=c.newInstance();
在运行时构造任意一个类的对象,这也是发射机制提供的
要从Result中取得数据要知道列名,要将取到的值存到对象中需要属性名
List<String> columnList=xml.getColumnList();
List<String> nameList=xml.getNameList();
/** 按参数队列读取记录中的数据 **/
for(int i=0;i<columnList.size();i++){
String paramName=nameList.get(i);
String columnLabel=columnList.get(i);
String methodName="set"+paramName.substring(0, 1).toUpperCase()+paramName.substring(1);
Field field=c.getDeclaredField(paramName);
要注意如果一个属性名是param,其set方法是setParam,但是要取到方法对象光有方法名还不够,因为set方法是有参数的,所以还需要对应的属性Field对象,同Method一样有
getField(fieldName)和getDeclaredField(fieldName)两个方法,前者可以取到public Field,包括从父类中继承的,后者取得所有在类中声明的,包括private,当然对于Java的封装性,属性通常是private,所以要用getDeclaredField
然后是取得这个属性的class,特别小心的是不要想当然地调用getClass()方法,那样你会得到java.lang.reflec.Field
我就试过,应该是调用getType()
Class paramClass=field.getType();
Method method=c.getMethod(methodName, paramClass);
然后取得set方法
在插入时不需太多考虑属性的类型,不过在查询时这是很重要的,应为要取得对应的类型必须调用ResultSet对应的方法
/** 根据参数类型调用对应方法 **/
if(paramClass.getSimpleName().equals("int")){
int args=rs.getInt(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("String")){
String args=rs.getString(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Array")){
Array args=rs.getArray(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("InputStream")){
InputStream args=rs.getAsciiStream(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("BigDecimal")){
BigDecimal args=rs.getBigDecimal(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Blob")){
Blob args=rs.getBlob(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("boolean")){
boolean args=rs.getBoolean(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("byte")){
byte args=rs.getByte(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Reader")){
Reader args=rs.getCharacterStream(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("byte[]")){
byte[] args=rs.getBytes(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Clob")){
Clob args=rs.getClob(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Date")){
Date args=rs.getDate(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("double")){
double args=rs.getDouble(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("float")){
float args=rs.getFloat(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("long")){
long args=rs.getLong(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Object")){
Object args=rs.getObject(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("NClob")){
NClob args=rs.getNClob(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Ref")){
Ref args=rs.getRef(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("short")){
short args=rs.getShort(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Time")){
Time args=rs.getTime(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("Timestamp")){
Timestamp args=rs.getTimestamp(columnLabel);
method.invoke(obj, args);
}else if(paramClass.getSimpleName().equals("URL")){
URL args=rs.getURL(columnLabel);
method.invoke(obj, args);
}
然后返回Object对象,完成,否则返回null
}
return obj;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return null;
这样插入,查询,更新就都实现了,不过现在还没解决一对多和多对多的关系,随后将继续完善