利用反射和xml配置文件手写一个小型的框架

1. 利用xml配置实体类和数据库表名的映射关系

  • sql语句
create table ticket_info (
       ticket_id number primary key,
       ticket_name varchar(30) not null,  
       ticket_price number(10, 2) not null     
)
create sequence ticket_seq

create table goods_info (
       goods_id number primary key, 
       goods_name varchar(30) not null,
       goods_price number(10, 2) not null,
       goods_date date not null,      
       goods_factory varchar(50) not null 
)
create sequence goods_seq
  • xml, 映射表属性和实体类列

  • goods.xml

<?xml version="1.0" encoding="UTF-8"?>

<class name="com.lovely.entity.Goods" table="goods_info">
	<id name="gid" column="goods_id">
		<sequence>goods_seq</sequence>
	</id>

	<property name="gname" column="goods_name"></property>
	<property name="gprice" column="goods_price"></property>
	<property name="gdate" column="goods_date"></property>
	<property name="gfactory" column="goods_factory"></property>
</class>

  • ticket.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- 实体类和表之间的映射关系 -->
<class name="com.lovely.entity.Ticket" table="ticket_info"> <!-- 类名和表名的映射关系 -->
	<id name="tid" column="ticket_id"> <!-- 实体主键和表中主键列的映射关系 -->
		<sequence>ticket_seq</sequence>
	</id>
	
	<property name="tname" column="ticket_name"></property> <!-- 属性名和表名列的映射关系 -->
	<property name="tprice" column="ticket_price"></property>

</class>
  • goods, 和 entity的实体类
package com.lovely.entity;

import java.sql.Date;

public class Goods {
	
	private Integer gid;
	private String gname;
	private Double gprice;
	private Date gdate;
	private String gfactory;
	
	public Goods() {}
	
	public Integer getGid() {
		return gid;
	}
	public void setGid(Integer gid) {
		this.gid = gid;
	}
	public String getGname() {
		return gname;
	}
	public void setGname(String gname) {
		this.gname = gname;
	}
	public Double getGprice() {
		return gprice;
	}
	public void setGprice(Double gprice) {
		this.gprice = gprice;
	}
	public Date getGdate() {
		return gdate;
	}
	public void setGdate(Date gdate) {
		this.gdate = gdate;
	}
	public String getGfactory() {
		return gfactory;
	}
	public void setGfactory(String gfactory) {
		this.gfactory = gfactory;
	}


}

package com.lovely.entity;

public class Ticket {
	
	private Integer tid;
	private String tname;
	private Double tprice;
	
	public Ticket() {}

	public Ticket(Integer tid, String tname, Double tprice) {
		super();
		this.tid = tid;
		this.tname = tname;
		this.tprice = tprice;
	}

	public Integer getTid() {
		return tid;
	}

	public void setTid(Integer tid) {
		this.tid = tid;
	}

	public String getTname() {
		return tname;
	}

	public void setTname(String tname) {
		this.tname = tname;
	}

	public Double getTprice() {
		return tprice;
	}

	public void setTprice(Double tprice) {
		this.tprice = tprice;
	}

	@Override
	public String toString() {
		return "Ticket [tid=" + tid + ", tname=" + tname + ", tprice=" + tprice
				+ "]\n";
	}
	
}

2. 根据xml设计,用正确的数据结构映射类封装好xml信息

  • 主键id
package com.lovely.base;

public class MapperId {
	// 实体id 映射 数据库id
	private String idName;
	private String idColumn;
	private String seqName;
	
	public String getIdName() {
		return idName;
	}
	public void setIdName(String idName) {
		this.idName = idName;
	}
	public String getIdColumn() {
		return idColumn;
	}
	public void setIdColumn(String idColumn) {
		this.idColumn = idColumn;
	}
	public String getSeqName() {
		return seqName;
	}
	public void setSeqName(String seqName) {
		this.seqName = seqName;
	}
	
	@Override
	public String toString() {
		return "MapperId [idName=" + idName + ", idColumn=" + idColumn
				+ ", seqName=" + seqName + "]";
	}

}


  • MapperData 映射数据类
package com.lovely.base;

import java.util.LinkedHashMap;

public class MapperData {
	// 映射文件类
	
	// 实体全类名
	private String className;
	// 表名
	private String tableName;
	private MapperId mapperId;
	// 存储除主键外 实体属性 -> 数据表列 相关信息
	private LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
	
	public String getClassName() {
		return className;
	}
	public void setClassName(String className) {
		this.className = className;
	}
	public String getTableName() {
		return tableName;
	}
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}
	public MapperId getMapperId() {
		return mapperId;
	}
	public void setMapperId(MapperId mapperId) {
		this.mapperId = mapperId;
	}
	public LinkedHashMap<String, String> getProperties() {
		return properties;
	}
	public void setProperties(LinkedHashMap<String, String> properties) {
		this.properties = properties;
	}
	
	@Override
	public String toString() {
		return "MapperData [className=" + className + ", tableName="
				+ tableName + ", mapperId=" + mapperId + ", properties="
				+ properties + "]\n";
	}


}

3. 得到数据库连接前,读取xml信息,用map封装成映射数据

  • 实体属性名和表的列名一样,也封装在map里面了
package com.lovely.dao;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.lovely.base.MapperData;
import com.lovely.base.MapperId;

public class BaseDao {
	
	static {
		try {
			Class.forName("oracle.jdbc.OracleDriver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * key: 全路径类名 
	 * value: 类和表的映射关系 MapperData
	 */
	public static HashMap<String, MapperData> map = new HashMap<String, MapperData>();
	
	static {
		// 静态块解析xml映射文件
		try {			
			Class<?> baseDaoClass = Class.forName("com.lovely.dao.BaseDao");
			// 得到xml路径
			URL url = baseDaoClass.getResource("/com/lovely/mapper");
			String path = url.getFile();
			
			File file = new File(path);
			// 拿到所有xml文件
			File[] files = file.listFiles();
			for (int i = 0; i < files.length; i++) {
				// 解析xml一个文件
				// System.out.println(files[i].getName());
				SAXReader reader = new SAXReader();
				Document doc = reader.read(files[i]);
				// 拿到 class结点
				Element root = doc.getRootElement();
				MapperData mapperData = new MapperData();
				// 保存实体类名
				mapperData.setClassName(root.attributeValue("name")); 
				// 保存数据表名
				mapperData.setTableName(root.attributeValue("table"));
				
				MapperId mapperId = new MapperId();
				
				// 主键结点映射关系
				Element primaryKey = root.element("id");
				// 实体类主键
				mapperId.setIdName(primaryKey.attributeValue("name"));
				// 表的主键
				mapperId.setIdColumn(primaryKey.attributeValue("column"));
				// 序列名称
				mapperId.setSeqName(primaryKey.elementText("sequence"));
				
				// 保存主键结点映射关系
				mapperData.setMapperId(mapperId);
				
				@SuppressWarnings("unchecked")
				// 所有 实体属性-表名结点映射关系
				List<Element> property = root.elements("property");
				
				LinkedHashMap<String, String> lhm = new LinkedHashMap<String, String>();
				for (Element field : property) {
					lhm.put(field.attributeValue("name"), field.attributeValue("column"));
				}
				
				mapperData.setProperties(lhm);
				
				// 把实体类 与 xml中的映射数据一一对应
				map.put(root.attributeValue("name"), mapperData);
			}
		
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	static {
		// 加载没有配置文件的实体类 
		
		try {
			Class<?> c = BaseDao.class;
			// mapper下的路径
			String path = c.getResource("/com/lovely/entity").getFile();
			
			File file = new File(path);
			File[] files = file.listFiles();
			for (int i = 0; i < files.length; i++) {
				String fileName = files[i].getName();
			
				String className = "com.lovely.entity." + fileName.substring(0, fileName.indexOf("."));
				
				// 没有映射文件的解析 对于数据库表名,字段,顺序 和 实体的类名 字段 顺序一样。
				if (!map.containsKey(className)) {
					
					// 实体类的类型描述
					Class<?> cc = Class.forName(className);
					
					MapperData value = new MapperData();
					
					// 设置实体类名和表名
					value.setClassName(cc.getName());
					value.setTableName(cc.getSimpleName());
					
					Field[] fields = cc.getDeclaredFields();
					Field.setAccessible(fields, true);
					
					// 拿到主键
					String primaryKeyName = fields[0].getName();
					
					MapperId mapperId = new MapperId();
					mapperId.setIdName(primaryKeyName);
					mapperId.setIdColumn(primaryKeyName);
					mapperId.setSeqName("seq_" + cc.getSimpleName());
					
					// 属性 主键列名 序列名
					value.setMapperId(mapperId);
					
					LinkedHashMap<String, String> properties = new LinkedHashMap<String, String>();
					
					// 设置除主键意外的属性
					for (int j = 1; j < fields.length; j++) {
						properties.put(fields[j].getName(), fields[j].getName());
					}
					
					value.setProperties(properties);
					
					map.put(className, value);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
		}

	}
	
	public static void main(String[] args) {
		System.out.println(map);
	}
	
	public static Connection getConn() {
		Connection conn = null;
		
		String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
		try {
			conn = DriverManager.getConnection(url, "scott", "scott");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return conn;
	}
	
	public static void closeAll(Connection conn, PreparedStatement ps, ResultSet rs) {
		try {
			if (rs != null)
				rs.close();
			if (ps != null)
				ps.close();
			if (conn != null)
				conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

4. 写dao时根据反射和map生成sql语句,拿到属性值

  • curd实现
package com.lovely.dao;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import com.lovely.base.MapperData;
import com.lovely.base.MapperId;

/**
 * 
 * @author echo lovely
 * 
 * 万能增删改查
 *
 */
public class CommonDao {
	
	public int save(Object entity) {
		int count = -1;
		
		Class<?> c = entity.getClass();
		MapperData mapperData = BaseDao.map.get(c.getName());
		
		StringBuffer sql = new StringBuffer();
		
		sql.append("insert into ");

		sql.append(mapperData.getTableName() + " values (");
		
		// insert into tableName values (seq_table.nextval, ?, ?, ?...)
		sql.append(mapperData.getMapperId().getSeqName() + ".nextval");
		
		LinkedHashMap<String,String> properties = mapperData.getProperties();
		Set<String> keySet = properties.keySet();
		
		// 除主键外的 实体属性名
		for (int i = 0; i < keySet.size(); i++) {
			sql.append(", ?");
		}
		
		sql.append(")");
		
		System.out.println(sql);
		
		Connection conn = BaseDao.getConn();
		PreparedStatement ps = null;
		
		try {
			ps = conn.prepareStatement(sql.toString());
			
			// 设置参数的值
			
			// 取实体列
			int index = 1;
			for (String entityColumn : keySet) {
				// 反射根据属性名称拿值
				Field field = c.getDeclaredField(entityColumn);
				field.setAccessible(true);
				ps.setObject(index, field.get(entity));
				index ++;
			}

			count = ps.executeUpdate();
		
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			BaseDao.closeAll(conn, ps, null);
		}
		
		return count;
	}
	
	public List<Object> queryAll(Class<?> c) {
		List<Object> list = new ArrayList<Object>();
		
		// 拿到  实体类 与 表的列 映射关系
		MapperData mapperData = BaseDao.map.get(c.getName());
		
		String sql = "select * from " + mapperData.getTableName();
		
		Connection conn = BaseDao.getConn();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		
		LinkedHashMap<String, String> properties = mapperData.getProperties();
		
		try {
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();
			
			// 表主键列名
			String primaryKeyName = mapperData.getMapperId().getIdColumn();
			String idName = mapperData.getMapperId().getIdName();
			
			while (rs.next()) {
				Object entity = c.newInstance();
				
				Object primaryKeyValue = rs.getObject(primaryKeyName);	
			
				// 主键属性
				Field primaryFiled = c.getDeclaredField(idName);
				
				if (primaryFiled.getType() == Integer.class)
					primaryKeyValue = rs.getInt(primaryKeyName);
				
				primaryFiled.setAccessible(true);
				primaryFiled.set(entity, primaryKeyValue);
				
				
				Set<Entry<String,String>> entrySet = properties.entrySet();
				
				for (Entry<String, String> entry : entrySet) {
					// 属性名称
					String filedName = entry.getKey();
					// 表的列名
					String columnName = entry.getValue();
					
					Object attributValue = rs.getObject(columnName);
					// System.out.println(filedName + "\t" + columnName + "...");
				
					// 除主键外的属性对象
					Field f = c.getDeclaredField(filedName);
					
					if (f.getType() == Double.class) {
						attributValue = rs.getDouble(columnName);
					} else if (f.getType() == java.sql.Timestamp.class) {
						attributValue = rs.getTimestamp(columnName);
					}
					
					f.setAccessible(true);
					f.set(entity, attributValue);
				}
				
				list.add(entity);			
				
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			BaseDao.closeAll(conn, ps, rs);
		}
		
		return list;
	}
	
	public Object queryOne(Object obj) {
		Object entity = null;
		Class<?> c = obj.getClass();

		MapperData mapperData = BaseDao.map.get(c.getName());
		
		String sql = "select * from " + mapperData.getTableName() + " where " + mapperData.getMapperId().getIdColumn() + " = ?";
		
		System.out.println(sql);
		Connection conn = BaseDao.getConn();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			MapperId mapperId = mapperData.getMapperId();
			
			ps = conn.prepareStatement(sql);
			
			// 拿到主键属性对象
			Field field = c.getDeclaredField(mapperId.getIdName());
			field.setAccessible(true);
			
			ps.setObject(1, field.get(obj));
			rs = ps.executeQuery();	
			
			LinkedHashMap<String,String> properties = mapperData.getProperties();
			Set<Entry<String, String>> entrySet = properties.entrySet();
			if (rs.next()) {
				// 记得反射创建对象...
				entity = c.newInstance();
				
				Field idFiled = c.getDeclaredField(mapperId.getIdName());
				idFiled.setAccessible(true);
				
				// 取到主键值
				Object idColumn = rs.getObject(mapperId.getIdColumn());
				System.out.println(idColumn + "\t" + mapperId.getIdName() + "\t" + mapperId.getIdColumn());
				
				if (idFiled.getType() == Integer.class) {
					idColumn = rs.getInt(mapperId.getIdColumn());
				}	
				
				idFiled.set(entity, idColumn);
				
				for (Entry<String, String> entry : entrySet) {
					
					Field f1 = c.getDeclaredField(entry.getKey());
					f1.setAccessible(true);
					Object value = rs.getObject(entry.getValue());
					
					if (f1.getType() == Double.class)
						value = rs.getDouble(entry.getValue());
					else if (f1.getType() == Timestamp.class) {
						value = rs.getTimestamp(entry.getValue());
					}
					
					
					f1.set(entity, value);		
				}
				

			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			BaseDao.closeAll(conn, ps, rs);
		}
		
		return entity;
	}
	
	public int update(Object entity) {
		int count = -1;
		Class<?> c = entity.getClass();
		
		StringBuffer sql = new StringBuffer();
		// 根据类的全路径 拿到MapperData
		MapperData mapperData = BaseDao.map.get(c.getName());
		
		// update tabName set * = ?, * = ?, * = ? .... where id = ?
		sql.append("update " + mapperData.getTableName() + " set ");
		
		LinkedHashMap<String,String> properties = mapperData.getProperties();
		// 除了主键外的所有属性集合
		Set<String> keySet = properties.keySet();
		
		// 数据库中表 列的集合
		Collection<String> cloumnNames = properties.values();
		
		int cloumnSize = cloumnNames.size();
		int index = 0;
		
		for (String cloumnName : cloumnNames) {
			if (index < cloumnSize - 1) 
				sql.append(cloumnName + " = ?, ");
			else 
				sql.append(cloumnName + " = ?");
			index ++;			
		}
		
		
		sql.append(" where " + mapperData.getMapperId().getIdColumn() + " = ?");
		
		System.out.println(sql);
		
		Connection conn = BaseDao.getConn();
		PreparedStatement ps = null;
		
		try {
			ps = conn.prepareStatement(sql.toString());
			
			int paramIndex = 1;
			for (String fieldAttribute : keySet) {
				// 映射的键 -> 实体属性对象
				Field field = c.getDeclaredField(fieldAttribute);
				field.setAccessible(true);
				Object obj = field.get(entity);
				ps.setObject(paramIndex, obj);
				paramIndex ++;
			}
			
			// 主键属性
			Field field = c.getDeclaredField(mapperData.getMapperId().getIdName());
			field.setAccessible(true);
			
			ps.setObject(paramIndex, field.get(entity));
			
			count = ps.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			BaseDao.closeAll(conn, ps, null);
		}
		
		
		return count;
	}
	
	public int delete(Object obj) {
		int count = -1;
		
		Class<?> c = obj.getClass();
		
		MapperData mapperData = BaseDao.map.get(c.getName());
		
		MapperId mapperId = mapperData.getMapperId();
		
		String sql = "delete from " + mapperData.getTableName() + " where " + mapperId.getIdColumn() + " = ?";
		
		Connection conn = BaseDao.getConn();
		PreparedStatement ps = null;
		
		try {
			ps = conn.prepareStatement(sql);
			
			// 反射拿到实体类 主键属性对象
			Field field = c.getDeclaredField(mapperId.getIdName());
			field.setAccessible(true);
			// 根据属性对象 取到 该对象的属性值
			ps.setObject(1, field.get(obj));
			
			count = ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			BaseDao.closeAll(conn, ps, null);
		}
		
		return count;		
	}

}

测试

  • 一句代码搞定查询。。。

package com.lovely.test;


import com.lovely.dao.CommonDao;
import com.lovely.entity.Student;
import com.lovely.entity.Ticket;

public class Test1 {
	
	public static void main(String[] args) {
		CommonDao dao = new CommonDao();
		// 学生类的结构和表的结构一样
		System.out.println(dao.queryAll(Studdent.getClass()));

		// 配置了xml映射关系的
		System.out.println(dao.queryAll(Ticket.class));
	}

}

[Student [sid=46, sname=jack, sgender=male, sbirth=2020-07-02 20:24:01.0, saddress=null, sinfo=null]
]
[Ticket [tid=2, tname=花木兰, tprice=33.2]
, Ticket [tid=3, tname=阿凡达2, tprice=50.5]
]

猜你喜欢

转载自blog.csdn.net/qq_44783283/article/details/107093086