使用自定义注解实现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类)

getAnnotation(Class<A> annotationClass)

          如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

getAnnotations()

          返回此元素上存在的所有注释。

getDeclaredAnnotations()

          返回直接存在于此元素上的所有注释。

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

          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

getDeclaredMethods()

          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

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

          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

getMethods()
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

getDeclaredField(String name)

          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

getDeclaredFields()

          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

getField(String name)

          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

getFields()
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

getName()

          以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

isAnnotation()

          如果此 Class 对象表示一个注释类型则返回 true。

isAnnotationPresent(Class<? extends Annotation> annotationClass)
          如果指定类型的注释存在于此元素上,则返回 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());
 
	}

}

结果:



数据库设计及数据



最后:可以看出通过自定义注解可以给我们在写数据库连接提供一种新的思路。相比之前我之前的提供的方法,有了较好的兼容性。

最后的最后:编写不易,如对你有用,请点个赞吧!




猜你喜欢

转载自blog.csdn.net/nut___/article/details/79705429