java的反射机制前面已经讲过,这里不再赘述,这篇文章将会利用反射,来实现一个通用的dao层。
我们先来看一下,通常,我们是如何写dao的。
public class Person { public int pid; public String pname; public double psalary; public Date pbirthday; public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } public double getPsalary() { return psalary; } public void setPsalary(double psalary) { this.psalary = psalary; } public Date getPbirthday() { return pbirthday; } public void setPbirthday(Date pbirthday) { this.pbirthday = pbirthday; } public Person() { super(); } }这是一个正常的javabean,这里的变量写成public不是我 不专业。。。而是我比较懒,方便后面写代码。。。
CREATE DATABASE reflect; USE reflect; CREATE TABLE person( pid INT PRIMARY KEY, pname VARCHAR(255) NOT NULL, psalary FLOAT, pbirthday DATE ); INSERT INTO person VALUES( 1,'jack','1000','1995-11-27' ); CREATE TABLE animal( aid INT PRIMARY KEY, aname VARCHAR(255) NOT NULL ); INSERT INTO animal VALUES( 1,'tom' );数据库脚本文件,这里采用mysql数据库
package com.mystery.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.mystery.domain.Person; public class PersonDao { public static List<Person> select() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; List<Person> list = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); st = conn.createStatement(); rs = st.executeQuery("select * from person"); list = new ArrayList<Person>(); while (rs.next()) { Person p = new Person(); p.setPid(rs.getInt("pid")); p.setPname(rs.getString("pname")); p.setPsalary(rs.getFloat("psalary")); p.setPbirthday(rs.getDate("pbirthday")); list.add(p); } } finally { if (rs != null) rs.close(); if (st != null) st.close(); if (conn != null) conn.close(); } return list; } @Test public void test(){ try { List<Person> l = select(); for (int i = 0; i < l.size(); i++) { System.out.println(l.get(i).getPname()); } } catch (SQLException e) { e.printStackTrace(); } } }通常,我们都是这么写的,代码量也不是很大,为了省事,我就只写一个select方法了。但是,假如我们又多了一张表,也就是animal,怎么办呢?正常情况下,我们是这么做的:
package com.mystery.domain; public class Animal { public int aid; public String aname; public int getAid() { return aid; } public void setAid(int aid) { this.aid = aid; } public String getAname() { return aname; } public void setAname(String aname) { this.aname = aname; } }话不多说,直接javabean
package com.mystery.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.mystery.domain.Animal; public class AnimalDao { public static List<Animal> select() throws SQLException { Connection conn = null; Statement st = null; ResultSet rs = null; List<Animal> list = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); st = conn.createStatement(); rs = st.executeQuery("select * from animal"); list = new ArrayList<Animal>(); while (rs.next()) { Animal a = new Animal(); a.setAid(rs.getInt("aid")); a.setAname(rs.getString("aname")); list.add(a); } } finally { if (rs != null) rs.close(); if (st != null) st.close(); if (conn != null) conn.close(); } return list; } @Test public void test(){ try { List<Animal> l = select(); for (int i = 0; i < l.size(); i++) { System.out.println(l.get(i).getAname()); } } catch (SQLException e) { e.printStackTrace(); } } }然后很无耻的copy&paste一下,稍微做一些修改就好了。感觉也不是很费力。但是,假如又多了10张表呢?copy&paste是那些勤快的人的写法,像我这种比较懒的,就喜欢一劳永逸的写法。
做个测试:
@Test public void testSelectAll(){ try { @SuppressWarnings("unchecked") //List<Person> l = (List<Person>) selectAll(Person.class,new Person()); List<Animal> l = (List<Animal>) selectAll(Animal.class); for (int i = 0; i < l.size(); i++) { System.out.println(l.get(i).getAname()); } } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }这样,一个方法就可以查询所有的对象,无论是animal还是person,一个dao就够了,是不是方便了许多?
再写一个插入和删除
/** * 插入功能 * @param obj 要插入的对象 * @throws Exception */ public void insert(Object obj) throws Exception { Connection conn = null; PreparedStatement st = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); Class<?> cls = obj.getClass(); Field[] fields = cls.getFields(); // 下面一段代码准备SQL语句的两部分。 StringBuilder sbForFieldName = new StringBuilder(); StringBuilder sbForQuestionMark = new StringBuilder(); for (int i = 0; i < fields.length; i++) { if(i>0) { sbForFieldName.append(","); sbForQuestionMark.append(","); } sbForFieldName.append(fields[i].getName()); sbForQuestionMark.append("?"); } String FieldNames = sbForFieldName.toString(); String QuestionMarks = sbForQuestionMark.toString(); // 安全起见,我们需要用prepareStatement处理用户输入。 // 但是因为类的名称是可以由程序员控制的,我们用String.format生成语句 st = conn.prepareStatement(String.format( "INSERT INTO %s(%s) values(%s)", cls.getSimpleName(), FieldNames, QuestionMarks)); //填充PreparedStatement for (int i = 0; i < fields.length; i++) { st.setObject(i + 1, fields[i].get(obj)); } st.executeUpdate(); } finally { if (st != null) st.close(); if (conn != null) { conn.close(); } } } /** * 根据id删除对象 出入的对象必须已经对id赋值并且域名称必须包含'id' * @param obj * @return * @throws Exception */ public Boolean delete(Object obj) throws Exception { Connection conn = null; Statement st = null; Class<?> cls = obj.getClass(); try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/reflect","root","123456"); st = conn.createStatement(); Field[] fields = cls.getFields(); Object id = null; for (Field field : fields) { if(field.getName().indexOf("id") != -1){ id = field.get(obj); break; } } st.executeUpdate(String.format( "DELETE FROM %s WHERE pid = %s;",cls.getSimpleName(),id )); } catch (Exception e) { e.printStackTrace(); }finally { if (st != null) st.close(); if (conn != null) conn.close(); } return true; } @SuppressWarnings("deprecation") @Test public void testInsert(){ Person p = new Person(); p.setPid(2); p.setPbirthday(new Date(0,1,1)); p.setPname("jery"); p.setPsalary(1200); try { insert(p); System.out.println("ok"); } catch (Exception e) { e.printStackTrace(); } } @SuppressWarnings("deprecation") @Test public void testDelete(){ Person p = new Person(); p.setPid(2); p.setPbirthday(new Date(0,1,1)); p.setPname("jery"); p.setPsalary(1200); try { delete(p); System.out.println("ok"); } catch (Exception e) { e.printStackTrace(); } }其他的功能就不贴出来了,源码我会放到我的github主页(https://github.com/CleverFan)上,感兴趣的可以去看一下。我也是个新手,有哪些地方不合理的欢迎大家批评指正,互相学习。
