1. 程式人生 > >JDBC中CommonDao的設計,策略模式和反射

JDBC中CommonDao的設計,策略模式和反射

普通方法:

CommonDao.java

public class CommonDao {
	/**
	 * 此方法是公共方法,專門獲取資料庫連線
	 * @return
	 */
	public static Connection getConnection(){
		Connection con=null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
	con=DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root","0118");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return con;
	}
	/**
	 * 此方法是公共方法,專門用來關閉資料庫資源
	 * @param rs
	 * @param pstmt
	 * @param con
	 */
	public static void closeAll(ResultSet rs,PreparedStatement pstmt,Connection con){
		  try {
			 if(rs!=null){
			   rs.close();
		   }if(pstmt!=null){
			   pstmt.close();
		   }if(con!=null){
			   con.close();
		   }
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

UserDao.java

public class UserDao implements UserDao{

	@Override
	public User login(String userName, String userPassword) throws SQLException {
		// TODO Auto-generated method stub
        User user = null;

		Connection con = CommonDao.getConnection();
		String sql="select id from t_user where username=? and password=?";
		PreparedStatement pstmt=con.prepareStatement(sql);
		pstmt.setString(1, userName);
		pstmt.setString(2,userPassword);
		ResultSet rs = pstmt.executeQuery();
		if(rs.next()){
            user = new User();
            user.setUserId(rs.getInt("user_id"));
            user.setUserName(rs.getString("user_name"));
            user.setUserPassword(rs.getString("user_password"));
        }
		CommonDao.closeAll(rs, pstmt, con);

		return user;
	}

	@Override
	public int addUser(User user) throws SQLException {
		// TODO Auto-generated method stub
        int rowAffect = 0;

		Connection con = CommonDao.getConnection();
		String sql="insert into t_user(username,password) values(?,?)";
		PreparedStatement pstmt = con.prepareStatement(sql);
		pstmt.setString(1, user.getUserName());
		pstmt.setString(2, user.getUserPassword());
		rowAffect = pstmt.executeUpdate(sql);
		CommonDao.closeAll(null, pstmt, con);
		
		return rowAffect ;
	}

優化:策略模式

UserDao中存在大量的重複程式碼,比如資料庫的連線與關閉,PrepareStatement的資料填充操作,不同的是sql語句和對ResultSet結果集的操作,所以可以使用策略模式對程式碼進行優化

RowMapper.java

public interface RowMapper<T> {
	
	public T mapRow(ResultSet rs) throws SQLException;

}

CommonDao.java

public class CommonDao {
	/**
	 * 此方法是公共方法,專門獲取資料庫連線
	 * @return
	 */
	public static Connection getConnection(){
		Connection con=null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
	con=DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root","0118");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return con;
	}
	/**
	 * 此方法是公共方法,專門用來關閉資料庫資源
	 * @param rs
	 * @param pstmt
	 * @param con
	 */
	public static void closeAll(ResultSet rs,PreparedStatement pstmt,Connection con){
		  try {
			 if(rs!=null){
			   rs.close();
		   }if(pstmt!=null){
			   pstmt.close();
		   }if(con!=null){
			   con.close();
		   }
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
/**
	 * 通用更新
	 * @param sql	DML語句
	 * @param params	引數
	 * @return
	 * @throws SQLException 
	 */
	public static int executeUpdate(String sql,Object...params) throws SQLException{
		int rowAffect = 0;
		
		Connection con = getConnection();
		PreparedStatement pstmt = con.prepareStatement(sql);
		if(params!=null){
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1, params[i]);
			}
		}
		rowAffect = pstmt.executeUpdate();
		closeAll(null, pstmt, con);
		
		return rowAffect;
		
	}
	
	/**
	 * 通用查詢
	 * @param sql
	 * @param params
	 * @param rm
	 * @return
	 * @throws SQLException 
	 */
	public static <T> List<T> executeQuery(String sql,RowMapper<T> rm,Object...params) throws SQLException{
		
		List<T> list = new ArrayList<T>();
		Connection con = getConnection();
		PreparedStatement pstmt = con.prepareStatement(sql);
		if(params!=null){
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1, params[i]);
			}
		}
		ResultSet rs = pstmt.executeQuery();
		while(rs.next()){
			T mapRow = rm.mapRow(rs);
			list.add(mapRow);
		}
		closeAll(rs, pstmt, con);
		
		return list;
		
	}
}

UserDao.java

public class UserMysql implements UserDao{

	@Override
	public User login(String userName, String userPassword) throws SQLException {
		// TODO Auto-generated method stub
		User user = null;

		String sql="select user_id from t_user where user_name=? and user_password=?";
		RowMapper<User> rm = new RowMapper<User>() {
			@Override
			public User mapRow(ResultSet rs) throws SQLException {
				User user = new User();

				user.setUserId(rs.getInt("user_id"));
				user.setUserName(rs.getString("user_name"));
				user.setUserPassword(rs.getString("user_password"));

				return user;
			}
		};
		Object[] params = {userName,userPassword};
		user = CommonDao.executeQuery(sql, rm, params).get(0);
		
		return user;
	}

	@Override
	public int addUser(User user) throws SQLException {
		// TODO Auto-generated method stub
        int rowAffect = 0;
		String sql="insert into t_user(user_name,user_password) values(?,?)";
        rowAffect = CommonDao.executeUpdate(sql, user.getUserName(),user.getUserPassword());

		return rowAffect;
	}
}

優化:反射1.0

對於通用查詢,雖然策略模式可以節省很多工作,但是每次都要去寫不同實現,還是比較費事,通過觀察可以發現,從資料庫中取出的結果集是和User實體類一 一對應的,所以可以使用反射對程式碼再一次進行優化

CommonDao.java

public class CommonDao {
	/**
	 * 此方法是公共方法,專門獲取資料庫連線
	 * @return
	 */
	public static Connection getConnection(){
		Connection con=null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
	con=DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root","0118");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return con;
	}
	/**
	 * 此方法是公共方法,專門用來關閉資料庫資源
	 * @param rs
	 * @param pstmt
	 * @param con
	 */
	public static void closeAll(ResultSet rs,PreparedStatement pstmt,Connection con){
		  try {
			 if(rs!=null){
			   rs.close();
		   }if(pstmt!=null){
			   pstmt.close();
		   }if(con!=null){
			   con.close();
		   }
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
/**
	 * 通用更新
	 * @param sql	DML語句
	 * @param params	引數
	 * @return
	 * @throws SQLException 
	 */
	public static int executeUpdate(String sql,Object...params) throws SQLException{
		int rowAffect = 0;
		
		Connection con = getConnection();
		PreparedStatement pstmt = con.prepareStatement(sql);
		if(params!=null){
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1, params[i]);
			}
		}
		rowAffect = pstmt.executeUpdate();
		closeAll(null, pstmt, con);
		
		return rowAffect;
		
	}
	
	/**
	 * 通用DQL,,反射版本
	 * @param sql	只能是DQL語句
	 * @param params	佔位符的值
	 * @param class 實體物件,物件儲存的是資料庫的資料
	 * @return
	 * @throws SQLException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	public static <T> List<T> executeQuery(String sql,Class<T> clazz,Object...params) throws Exception{
		
		List<T> list = new ArrayList<T>();
		Connection con = getConnection();
		PreparedStatement pstmt = con.prepareStatement(sql);
		if(params!=null){
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1, params[i]);
			}
		}
		ResultSet rs = pstmt.executeQuery();
		
		//獲取結果集的元資料
		ResultSetMetaData rsmd = rs.getMetaData();
		//從結果的元資料中獲取結果集的元資料,元資料就是結果集的列資訊
		List<String> columnNames = new ArrayList<String>();//儲存結果的列頭的名字
		for(int i=0;i<rsmd.getColumnCount();i++){
			columnNames.add(rsmd.getColumnLabel(i+1));
		}
		
		while(rs.next()){
			//例項化T型別的實體物件
			T t = clazz.newInstance();
			//用反射呼叫t中的set方法
			for(String columnName:columnNames){
				//類似於setId setUserName setUserPassword
				String setterName = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
				Method[] methods = clazz.getDeclaredMethods();
				for(Method m:methods){
						m.invoke(t, value);
					}
				}
			}
			list.add(t);
		}
		closeAll(rs, pstmt, con);
		
		return list;
	}
}

UserDao.java

public class UserDaoImpl implements UserDao{

	@Override
	public int login(String userName, String userPassword) throws Exception {
		// TODO Auto-generated method stub
		User user = null;
		
		String sql="select user_id as userId from t_user where username=? and password=?";
		Object[] params = new Object[] {userName,userPassword};
		List<User> list = CommonDao.executeQuery(sql, User.class, params);
                user = list.get(0);
        
   		return user;
	}

	
	@Override
	public int addUser(User user) throws SQLException {
		// TODO Auto-generated method stub
        int rowAffect = 0;

	String sql="insert into t_user(user_name,user_password) values(?,?)";
        rowAffect = CommonDao.executeUpdate(sql,user.getUserName(),user.getUserPassword());
		
        return rowAffect;
	}

這樣一來,UserDao中方法的程式碼又少了很多,但是上面反射有一個問題就是,每次獲取方法名時都要將columnNames遍歷一遍,這樣的話對於資源的消耗會比較大,所以。。。。

優化:反射2.0

Class中還有一個方法就是 Method (String name, Class<?>... parameterTypes),可以直接通過方法名獲取類的方法,那麼問題來了,方法還需要查詢方法引數的類型別,這個。。。

解決:ResultSetMetaData這個物件有兩個方法 int (int column) 和 String (int column)

方法

 

檢視colType和colTypeName

註釋大概意思是colType是java.sql.Type中的型別,colTypeName是資料來源的型別名稱

如果想讓程式有更好的相容性,那麼colTypeName肯定是不行的,因為不同的資料庫欄位的型別名稱也不同,但是java.sql.Types就不一樣了,這是jdk定義的型別,屬於java的十三種規範之一JDBC,不同的資料庫廠商都會按照這個標準去做資料庫驅動

java.sql.Types的原始碼

/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

 *
 */

package java.sql;

/**
 * <P>The class that defines the constants that are used to identify generic
 * SQL types, called JDBC types.
 * <p>
 * This class is never instantiated.
 */
public class Types {

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>BIT</code>.
 */
        public final static int BIT             =  -7;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>TINYINT</code>.
 */
        public final static int TINYINT         =  -6;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>SMALLINT</code>.
 */
        public final static int SMALLINT        =   5;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>INTEGER</code>.
 */
        public final static int INTEGER         =   4;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>BIGINT</code>.
 */
        public final static int BIGINT          =  -5;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>FLOAT</code>.
 */
        public final static int FLOAT           =   6;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>REAL</code>.
 */
        public final static int REAL            =   7;


/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>DOUBLE</code>.
 */
        public final static int DOUBLE          =   8;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>NUMERIC</code>.
 */
        public final static int NUMERIC         =   2;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>DECIMAL</code>.
 */
        public final static int DECIMAL         =   3;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>CHAR</code>.
 */
        public final static int CHAR            =   1;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>VARCHAR</code>.
 */
        public final static int VARCHAR         =  12;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>LONGVARCHAR</code>.
 */
        public final static int LONGVARCHAR     =  -1;


/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>DATE</code>.
 */
        public final static int DATE            =  91;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>TIME</code>.
 */
        public final static int TIME            =  92;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>TIMESTAMP</code>.
 */
        public final static int TIMESTAMP       =  93;


/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>BINARY</code>.
 */
        public final static int BINARY          =  -2;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>VARBINARY</code>.
 */
        public final static int VARBINARY       =  -3;

/**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>LONGVARBINARY</code>.
 */
        public final static int LONGVARBINARY   =  -4;

/**
 * <P>The constant in the Java programming language
 * that identifies the generic SQL value
 * <code>NULL</code>.
 */
        public final static int NULL            =   0;

    /**
     * The constant in the Java programming language that indicates
     * that the SQL type is database-specific and
     * gets mapped to a Java object that can be accessed via
     * the methods <code>getObject</code> and <code>setObject</code>.
     */
        public final static int OTHER           = 1111;



    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>JAVA_OBJECT</code>.
     * @since 1.2
     */
        public final static int JAVA_OBJECT         = 2000;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>DISTINCT</code>.
     * @since 1.2
     */
        public final static int DISTINCT            = 2001;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>STRUCT</code>.
     * @since 1.2
     */
        public final static int STRUCT              = 2002;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>ARRAY</code>.
     * @since 1.2
     */
        public final static int ARRAY               = 2003;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>BLOB</code>.
     * @since 1.2
     */
        public final static int BLOB                = 2004;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>CLOB</code>.
     * @since 1.2
     */
        public final static int CLOB                = 2005;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * <code>REF</code>.
     * @since 1.2
     */
        public final static int REF                 = 2006;

    /**
     * The constant in the Java programming language, somtimes referred to
     * as a type code, that identifies the generic SQL type <code>DATALINK</code>.
     *
     * @since 1.4
     */
    public final static int DATALINK = 70;

    /**
     * The constant in the Java programming language, somtimes referred to
     * as a type code, that identifies the generic SQL type <code>BOOLEAN</code>.
     *
     * @since 1.4
     */
    public final static int BOOLEAN = 16;

    //------------------------- JDBC 4.0 -----------------------------------

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>ROWID</code>
     *
     * @since 1.6
     *
     */
    public final static int ROWID = -8;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>NCHAR</code>
     *
     * @since 1.6
     */
    public static final int NCHAR = -15;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>NVARCHAR</code>.
     *
     * @since 1.6
     */
    public static final int NVARCHAR = -9;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>LONGNVARCHAR</code>.
     *
     * @since 1.6
     */
    public static final int LONGNVARCHAR = -16;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>NCLOB</code>.
     *
     * @since 1.6
     */
    public static final int NCLOB = 2011;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type <code>XML</code>.
     *
     * @since 1.6
     */
    public static final int SQLXML = 2009;

    //--------------------------JDBC 4.2 -----------------------------

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type {@code REF CURSOR}.
     *
     * @since 1.8
     */
    public static final int REF_CURSOR = 2012;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * {@code TIME WITH TIMEZONE}.
     *
     * @since 1.8
     */
    public static final int TIME_WITH_TIMEZONE = 2013;

    /**
     * The constant in the Java programming language, sometimes referred to
     * as a type code, that identifies the generic SQL type
     * {@code TIMESTAMP WITH TIMEZONE}.
     *
     * @since 1.8
     */
    public static final int TIMESTAMP_WITH_TIMEZONE = 2014;

    // Prevent instantiation
    private Types() {}
}

其中不同的數字對應了不同的資料型別

如果還不太清楚的話可以參照 Java資料型別和MySql資料型別對應

那麼

CommonDao.java

/**
	 * 通用查詢
	 * @param sql
	 * @param clazz
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> executeQuery(String sql,Class<T> clazz,Object...params)throws Exception{
		
			Connection con = getConnection();
			PreparedStatement pstmt = con.prepareStatement(sql);
			List<T> list = new ArrayList<T>();
			if(params!=null){
				for(int i=0;i<params.length;i++){
					pstmt.setObject(i+1, params[i]);
				}
			}
			ResultSet rs = pstmt.executeQuery();
			//獲取結果集元資料
			ResultSetMetaData metaData = rs.getMetaData();
			while(rs.next()){
				T t = clazz.newInstance();
				for(int i=1;i<=metaData.getColumnCount();i++){
					String columnName = metaData.getColumnLabel(i);
					String methodName = "set"+columnName.substring(0, 1).toUpperCase()+columnName.substring(1);
					Class<?> paramType = null;
					if(metaData.getColumnType(i)==4){
						paramType=Integer.class;
					}else if(metaData.getColumnType(i)==12){
						paramType=String.class;
					}
					Method method = clazz.getDeclaredMethod(methodName, new Class[]{paramType});
					method.invoke(t, rs.getObject(columnName));
				}
				list.add(t);
			}
			return list;
	}	

在這個Demo中我只使用了兩種資料型別,所以並沒有寫過多的程式碼,當然,也可以根據自己的需要,將型別的判斷寫成一個方法,去進行型別的選擇