自定义注解并映射生成sql

时间:2022-05-09 12:00:36

注解是项目中经常用到的技术,常见的有java原生注解,像@overwrite等,还有像spring 中的@RequestMapping()这样的第三方注解,下面说一下自定义注解。理论的我也知之甚少,就不啰嗦,直接贴代码吧。 

需求:想实现项hiberate那样通过一个简单的注解,直接生成Sql语句。

一:创建实体类,并加上自定义的注解

package annotation.pojo;


import annotation.ann.MyColome;
import annotation.ann.MyTable;


@MyTable("t_user")
public class User {
	@MyColome("id")
	private String id;
	
	@MyColome("user_name")
	private String userName;
	
	@MyColome("tel")
	private String tel;
	
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	
	
}
此时添加的自定义注解并没有被声明,会报错,不过没关系,下边就创建这两个注解。

二:创建自定义注解

@Target({ElementType.TYPE})	//作用在类或接口上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {

	String value();
}
@Target({ElementType.FIELD})	//作用在字段上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyColome {

	String value();
}

创建注解其实主要的就看两个方面,一是作用域 @Target ,第二是 @Retention 定义注解的保留策略

@Target(ElementType.TYPE)    // 接口、类、枚举、注解
@Target(ElementType.FIELD)  // 字段、枚举的常量
@Target(ElementType.METHOD)  // 方法
@Target(ElementType.PARAMETER)  // 方法参数
@Target(ElementType.CONSTRUCTOR)   // 构造函数
@Target(ElementType.LOCAL_VARIABLE) // 局部变量
@Target(ElementType.ANNOTATION_TYPE) // 注解
@Target(ElementType.PACKAGE) / //    

@Retention(RetentionPolicy. SOURCE)    //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy. CLASS)       // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)   // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Document:说明该注解将被包含在javadoc中   //也就是自动生成API时用到

@Inherited:说明子类可以继承父类中的该注解   //接口的实现类是集成不了的,只有继承了父类的子类才能集成

三 使用注解,动态生成sql

在此我模拟了三组查询,根据id,name去查用户信息,最后又增加一个学生实体类,验证注解的通用性

实现思路:

1.获取表名:表名我是现在@Mytable("t_user") 通过查询条件获取当前对象的class,通过boolean isExist =c.isAnnotationPresent(MyTable.class);这行代码判断此对象中是否用到这个注解,如果有用到,就可以拿到并实例化这个注解对象,通过这个注解对象就可以拿到注解中定义的值。

2.获取数据库字段名:其实与上边类似,通过该对象的class获取他的所有属性,然后再一次判断属性是否用到注解,

等等等一样的逻辑。

3.获取查询条件的值:拿到所有属性后,就可以通过属性的get方法应用java反射那对对应的属性值,具体见代码。

4.其他的就是把拿到的这些值,拼接到一起

public class UserDao {
	public static void main(String[] args) {
		User u1 = new User();
		u1.setId("10");		//根据ID查询用户信息
		
		User u2 = new User();
		u2.setUserName("nsh");	//根据用户名查询
		
		Student s = new Student();
		s.setId("2");
		
		
		String sql1 =query(u1);
		String sql2 =query(u2);
		String sql3 =query(s);
		System.out.println("sql1="+sql1);
		System.out.println("sql2="+sql2);
		System.out.println("sql3="+sql3);
	}

	private static String query(Object u) {
		StringBuffer sb = new StringBuffer();
		//1.获取class
		Class c =u.getClass();
		//2.获取tableName
		//判断是否有table这个注解
		boolean isExist =c.isAnnotationPresent(MyTable.class);
		if(isExist){
			//获取到table这个注解,并取得表名
			MyTable mt =(MyTable) c.getAnnotation(MyTable.class);
			String tableName =mt.value();
			sb.append("select * from ").append(tableName).append(" where 1=1");
		}
		//3遍历所有字段
		Field[] fArray= c.getDeclaredFields();
		for(Field f:fArray){
			//拿到字段后与实体类中的属性匹配,并得到其get方法,用来获取他的属性值
			String getMethodName ="";
			boolean isCExist =f.isAnnotationPresent(MyColome.class);
			if(isCExist){
				MyColome mc =f.getAnnotation(MyColome.class);
				String columeName =mc.value();	//字段对应数据库名字
				String name =f.getName();		//字段名字
				String value=null;				//字段值
				getMethodName="get"+name.substring(0,1).toUpperCase()+name.substring(1);//拼接属性的get方法
				try {
					Method m =c.getMethod(getMethodName);
					value =(String)m.invoke(u);		//拿到属性的值
					if(value == null || "".equals(value)){	//如果属性没值,不拼接sql
						continue;
					}
					else if(value instanceof String){
						value ="'"+value+"'";
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				sb.append(" and ").append(columeName).append(" = ").append(value);
			}
		}
		return sb.toString();
	}

}
四:测试
自定义注解并映射生成sql