使用自定义注解实现Dao 层

本文介绍如何使用自定义注解实现JDBC中的DAO层,通过注解增强代码的可读性和灵活性。讲解了自定义注解的语法要求,并通过查询学生信息的例子展示其应用。同时提及了相关类如Class的注解操作方法,强调自定义注解能为数据库连接提供新的设计思路,提高代码的兼容性。

定义(百度词条)

注解(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());
 
	}

}

结果:



数据库设计及数据



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

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




<think>好的,我现在需要帮助用户理解如何使用@Repository注解来声明DAO组件。根据用户提供的引用内容,我需要结合这些例子来给出清晰的回答。 首先,用户引用了三个不同的来源。引用[1]提到通过继承JpaRepository来创建仓库接口,这可能和Spring Data JPA相关。引用[2]展示了在DAO实现类上使用@Repository注解,并指定value属性。引用[3]则是一个更简单的例子,直接在类上添加@Repository,并包含一个查询方法。 我需要将这些信息整合起来,说明@Repository的不同用法。首先,@Repository的作用是将类标记为数据访问组件,同时处理异常转换,这是Spring框架的一个特性。然后,可能需要分步骤说明:比如基础用法、结合JPA的情况、以及自定义实现的情况。 注意到用户的问题是关于DAO的声明,所以应该涵盖接口和实现类两种情况。比如,引用[1]中的UserRepository是接口,而引用[2]和[3]是具体的实现类。这时候需要指出,在使用Spring Data JPA时,通常不需要写实现类,因为框架会自动生成。但如果是自定义DAO,则需要用@Repository标注实现类。 还要注意代码示例的正确性。例如,引用[3]中的UserRepository类可能更接近一个传统的DAO实现,而不是使用JPA的情况。需要区分这两种情况,告诉用户在不同场景下如何应用注解。 另外,异常处理部分,需要提到@Repository注解会将数据访问异常转换为Spring的统一异常体系,这点应该在回答中强调,因为这是该注解的一个重要功能。 最后,生成的相关问题需要围绕用户可能的后续疑问,比如注解的作用、与其它注解的区别、异常处理机制等。 在组织回答时,应该分步骤,先介绍注解的作用,再给出不同情况下的示例,最后总结注意事项。确保每个示例都正确使用LaTeX格式,行内代码用$...$,独立代码块用```...```,但根据用户提供的系统指令,似乎代码块需要用更明确的格式,比如Python示例中的方式,用三个反引号包围。 可能还需要检查引用标注是否正确,比如引用[1]、[2]、[3]对应的内容是否在回答中正确引用,并在末尾添加引用标识。 现在需要把这些思考整理成一个结构清晰的中文回答,确保每个部分都符合用户的要求,并正确引用提供的参考资料。</think>### 使用@Repository注解声明DAO组件的步骤及示例 #### 1. @Repository注解的作用 - **标记数据访问组件**:将类标识为DAO组件,由Spring容器管理[^3]。 - **异常自动转换**:将数据访问抛出的原生异常(如JDBC的SQLException)转换为Spring的统一数据访问异常体系[^3]。 --- #### 2. 基础用法示例 ##### 场景1:自定义DAO实现类 ```java import org.springframework.stereotype.Repository; @Repository // 标记为DAO组件 public class UserDao { public User findById(Long id) { // 模拟数据库查询 return new User(id, "John Doe"); } } ``` **说明**:直接标注在实现类上,Spring会自动扫描并注册为Bean。 --- ##### 场景2:结合Spring Data JPA接口 ```java import org.springframework.data.jpa.repository.JpaRepository; // 无需显式添加@Repository,因为JpaRepository接口的实现由Spring自动生成[^1] public interface UserRepository extends JpaRepository<User, Long> { // 可自定义查询方法 } ``` **说明**:继承`JpaRepository`的接口会自动被Spring识别为DAO组件,无需手动添加注解。 --- ##### 场景3:自定义DAO实现类(显式命名Bean) ```java @Repository("userDao") // 指定Bean名称 public class UserDaoImpl implements UserDao { @Override public User findUser(Long id) { // 具体数据库操作 return new User(id, "Alice"); } } ``` **说明**:通过`value`属性指定Bean名称,便于在其他地方通过名称注入[^2]。 --- #### 3. 结合其他注解的完整示例 ```java @Repository public class UserDaoImpl { @Autowired private JdbcTemplate jdbcTemplate; // 注入数据库操作工具 public User getUserById(Long id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, User.class); } } ``` **关键点**: - 通常与`@Autowired`或`@Resource`配合使用,注入依赖(如`JdbcTemplate`)。 - 若使用JPA,可直接通过方法命名规则定义查询,无需手动编写SQL。 --- #### 4. 注意事项 1. **包扫描配置**:确保`@Repository`所在包被`@ComponentScan`扫描到。 2. **与`@Component`的区别**:`@Repository`是`@Component`的特化,专用于数据访问,且具有异常转换功能。 3. **事务管理**:DAO通常结合`@Transactional`注解实现事务控制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值