通用的增删改查
- 1. 利用xml配置实体类和数据库表名的映射关系
- 2. 根据xml设计,用正确的数据结构映射类封装好xml信息
- 3. 得到数据库连接前,读取xml信息,用map封装成映射数据
- 4. 写dao时根据反射和map生成sql语句,拿到属性值
- 测试
- 为了解决上一篇文中实体类的局限性:不可加字段;实体名,表名,列的字段一样,而且顺序一样
- 上一篇反射实现dao层增删改查
- 本文实现结构
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_seqcreate 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;}@Overridepublic String toString() {return "Ticket [tid=" + tid + ", tname=" + tname + ", tprice=" + tprice+ "]\n";}}
2. 根据xml设计,用正确的数据结构映射类封装好xml信息
- 主键id
package com.lovely.base;public class MapperId {// 实体id 映射 数据库idprivate 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;}@Overridepublic 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;}@Overridepublic 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();// 根据类的全路径 拿到MapperDataMapperData 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]
]