1. 程式人生 > >使用自定義註解實現Dao 層

使用自定義註解實現Dao 層

定義(百度詞條)

註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。

       比如我們最常見的 @ Override 就是註解,除了JDK以及第三方註解,可以通過自定義註解來實現一些意想不到的效果。在這裡我們使用自定義註解來實現JDBC中的Dao層,通過例子可以體會自定義註解的妙用。

首先先看自定義註解的語法要求

@Target({Element.METHOD,Element.TYPE}) //註解作用的位置
@Retention(RUNTIME)//註解作用的時間

//使用@interface鍵字定義註解
Public @interface Description{
//成員以無參無異常方式宣告
   String desc();
   String author();
//可以用default為成員制定一個預設值
   Int age() default 18; 
}

關於自定義註解還有以下注意:

1、成員型別是受限的,合法的型別包括原始型別及String Class Annotation Enumeration

2、如果註解只有一個成員,則成員必須取名為value()在使用時可以忽略成員名和賦值號(=)

3、註解類可以沒有成員 沒有成員的註解成為標識註解

下面通過查詢學生資訊的例子,來展示自定義註解的妙用。

//關於表名的註解
package test;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
/** *@author Nut *@version 2018年3月26日 下午10:48:49 *自定義註解: *通過獲取value的值,可以得到要使用的資料庫中的表名 *描述使用者表的註解 */ public @interface TableName { String value(); }
關於表中列名的註解
package test;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
/** *@author Nut *@version 2018年3月26日 下午10:51:47 *自定義註解: *通過獲取value的值,可以獲取資料庫的表的列名 *描述使用者表屬性欄位的註解 */ public @interface ColumnName { String value(); }


//一個Student學生類
package test;

/**
 * @author Nut
 * @version 2018年3月26日 下午10:37:12 關於學生的
 */

@TableName("student")
public class Student {

	// 學生ID
	@ColumnName("ID")
	private String stuid;
	// 學生姓名
	@ColumnName("name")
	private String stuname;
	// 學生年齡
	@ColumnName("age")
	private int stuage;
	// 學生出生地
	@ColumnName("birthcity")
	private String stubirthcity;
	// 學生郵箱
	@ColumnName("email")
	private String stuemail;

	public Student() {
	}

	public Student(String stuID, String stuName, int stuAge, String stuBirthCity, String email) {
		super();
		this.stuid = stuID;
		this.stuname = stuName;
		this.stuage = stuAge;
		this.stubirthcity = stuBirthCity;
		this.stuemail = email;
	}

	public String getStuid() {
		return stuid;
	}

	public void setStuid(String stuid) {
		this.stuid = stuid;
	}

	public String getStuname() {
		return stuname;
	}

	public void setStuname(String stuname) {
		this.stuname = stuname;
	}

	public int getStuage() {
		return stuage;
	}

	public void setStuage(int stuage) {
		this.stuage = stuage;
	}

	public String getStubirthcity() {
		return stubirthcity;
	}

	public void setStubirthcity(String stubirthcity) {
		this.stubirthcity = stubirthcity;
	}

	public String getStuemail() {
		return stuemail;
	}

	public void setStuemail(String stuemail) {
		this.stuemail = stuemail;
	}

}
//下面是最重要的!!!

一些相關函式(Class類)

(Class<A> annotationClass)

          如果存在該元素的指定型別的註釋,則返回這些註釋,否則返回 null。

          返回此元素上存在的所有註釋。

          返回直接存在於此元素上的所有註釋。

          返回一個 Method 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告方法。

          返回 Method 物件的一個數組,這些物件反映此 Class 物件表示的類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。

(String name, Class<?>... parameterTypes)

          返回一個 Method 物件,它反映此 Class 物件所表示的類或介面的指定公共成員方法。


          返回一個包含某些 Method 物件的陣列,這些物件反映此 Class 物件所表示的類或介面(包括那些由該類或介面宣告的以及從超類和超介面繼承的那些的類或介面)的公共 member 方法。

          返回一個 Field 物件,該物件反映此 Class 物件所表示的類或介面的指定已宣告欄位。

          返回 Field 物件的一個數組,這些物件反映此 Class 物件所表示的類或介面所宣告的所有欄位。

          返回一個 Field 物件,它反映此 Class 物件所表示的類或介面的指定公共成員欄位。


          返回一個包含某些 Field 物件的陣列,這些物件反映此 Class 物件所表示的類或介面的所有可訪問公共欄位。

          以 String 的形式返回此 Class 物件所表示的實體(類、介面、陣列類、基本型別或 void)名稱。

          如果此 Class 物件表示一個註釋型別則返回 true。


          如果指定型別的註釋存在於此元素上,則返回 true,否則返回 false。

package test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author Nut
 * @version 2018年3月27日 上午8:48:26 
 * 通過傳入要查詢的條件返回sql語句 
 * 優點在於,無論返回ID,還是返回姓名,只需要用一個函式即可
 */
public class returnSql {

	/**
	 * @param obj
	 * @return 返回查詢語句
	 */
	public static String testQuery(Object obj) {
		// 儲存要返回的sql語句
		StringBuilder sb = new StringBuilder();
		Class<?> c1 = obj.getClass();
		/*
		 * 是否包含TableName型別的註解 對於資料庫是保證是否包含資料庫中的表
		 */
		if (!c1.isAnnotationPresent(TableName.class)) {
			return null;
		}
		// 獲取表的名字
		TableName t = (TableName) c1.getAnnotation(TableName.class);
		String tableName = t.value();
		// 將表名拼接到sql語句
		// 1=1保證sql語句在沒有查詢條件時正常
		sb.append("select * from ").append(tableName).append(" where 1=1");
		//
		Field[] fields = c1.getDeclaredFields();
		// 通過判斷field的不同型別,分別處理,拼接sql語句
		for (Field field : fields) {
			/*
			 * 是否包含ColumnName的註解 對於資料庫就是是否包含表中列名
			 */
			if (!field.isAnnotationPresent(ColumnName.class)) {
				continue;
			}
			// 獲取列的名字
			ColumnName cn = (ColumnName) field.getAnnotation(ColumnName.class);
			String columName = cn.value();
			// 通過get方法,得到值
			Object methodValue = null;
			String methodName = field.getName();
			String getMethodName = "get" + methodName.substring(0, 1).toUpperCase()
					+ methodName.substring(1).toLowerCase();
			try {
				Method method = c1.getMethod(getMethodName);
				methodValue = method.invoke(obj);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (methodValue == null || (methodValue instanceof Integer && (Integer) methodValue == 0)) {
				continue;
			}
			sb.append(" and ").append(columName);
			if (methodValue instanceof String) {
				if (((String) methodValue).contains(",")) {
					String[] s = ((String) methodValue).split(",");
					sb.append(" in(");
					for (String string : s) {
						sb.append("'").append(string).append("',");
					}
					sb.deleteCharAt(sb.length() - 1);
					sb.append(")");
				} else {
					sb.append("='").append(methodValue).append("'");
				}
			} else if (methodValue instanceof Integer) {
				sb.append("=").append(methodValue);
			}
		}
		return sb.toString();
	}

}
//這是這是一個數據庫連線Class

關於這部分可以詳見我的另一篇部落格JDBC詳解

package test;

/**
*@author Nut
*@version 2018年3月26日 下午10:58:37
*/
import java.sql.*;

public class DBhelper {

	// SqlServer資料庫驅動
	private static String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";

	// SqlServer資料庫連結地址
	private static String dbURL = "jdbc:sqlserver://localhost:1433;DatabaseName=Stu";// ?為你的資料庫名稱

	// 資料庫名稱
	private static String userName = "sa";// ? = 你的資料庫名稱

	// 資料庫密碼
	private static String userPwd = "*****";// ? = 你的資料庫密碼

	private static Connection conn = null;

	// 靜態程式碼塊載入資料庫驅動
	static {
		try {
			Class.forName(driverName);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	// 通過單例模式返回資料庫連線
	public static Connection getConnection() throws SQLException {

		if (conn == null) {
			conn = DriverManager.getConnection(dbURL, userName, userPwd);
		}
		return conn;

	}

	public static void main(String[] args) {

		Connection conn;
		try {
			conn = DBhelper.getConnection();
			if (conn != null) {
				System.out.println("資料庫連線成功");
			} else {
				System.out.println("資料庫連線失敗");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}
//執行sql語句
package test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author Nut
 * @version 2018年3月27日 下午1:02:23 通過returnSql返回的查詢語句來查詢資料庫
 */
public class executeSql {
	/*
	 * select語句查詢
	 */
	public static Student executeQuerySql(Object obj) {
		Connection conn = null; // 資料庫連線
		PreparedStatement stmt = null; // 預編譯 執行速度相對較快
		ResultSet rs = null; // 結果集

		try {
			conn = DBhelper.getConnection();// 實現資料庫連線
			String sql = returnSql.testQuery(obj);
			// 輸入SQL語句,通過+來實現資料的動態傳入
			stmt = conn.prepareStatement(sql); // 預編譯SQL語句
			rs = stmt.executeQuery();

			// 用於產生單個結果集的語句,例如 SELECT 語句。 被使用最多的執行 SQL 語句的方法是
			// executeQuery。這個方法被用來執行 SELECT 語句,它幾乎是使用最多的 SQL 語句

			if (rs.next()) {
				Student student = new Student();// 例項一個物件
				student.setStuid(rs.getString("ID"));
				student.setStuname(rs.getString("name"));
				student.setStuage(rs.getInt("age"));
				student.setStubirthcity(rs.getString("birthcity"));
				student.setStuemail(rs.getString("email"));
				// rs.getString("D#")獲取結果集中的值,通過SETTER函式複製
				return student;// 返回物件
			}
			return null;
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		} finally {
			// 關閉結果集
			if (rs != null) {
				try {
					rs.close();
					rs = null; // 賦值為null 相當於刪除
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if (stmt != null) {
				try {
					stmt.close();
					stmt = null;
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}//結束

}
//測試類
package test;
/**
*@author Nut
*@version 2018年3月27日 下午1:00:53
*/
public class test {

	public static void main(String[] args) {
		Student s1 = new Student();
		s1.setStuname("A");
		Student newStu1 = executeSql.executeQuerySql(s1);
		System.out.println(newStu1.getStuid());
		
		Student s2 = new Student();
		s2.setStuid("s003");
		Student newStu2 = executeSql.executeQuerySql(s2);
		System.out.println(newStu2.getStuname());
 
	}

}

結果:



資料庫設計及資料


最後:可以看出通過自定義註解可以給我們在寫資料庫連線提供一種新的思路。相比之前我之前的提供的方法,有了較好的相容性。

最後的最後:編寫不易,如對你有用,請點個贊吧!