Java反射机制实现Hibernate 三

现在要解决的问题,是获得一个未知类型对象的类名,调用该类未知的方法,还要取得其未知的属性,只要这个类已经配置好在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;

 

这样插入,查询,更新就都实现了,不过现在还没解决一对多和多对多的关系,随后将继续完善

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值