jpa使用注解传递对象_注解的运用

内容介绍:

  • 自定义Junit框架
  • 山寨JPA

自定义Junit框架

请大家始终牢记,用到注解的地方,必然存在三角关系,,并且别忘了设置保留策略为RetentionPolicy.RUNTIME。

代码结构

b785ca1bfb2f9a31eb7fb8b6443d8f0e.png

MyBefore注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBefore {
}

MyTest注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}

MyAfter注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAfter {
}

EmployeeDAOTest

//EmployeeDAO的测试类
public class EmployeeDAOTest {
	@MyBefore
	public void init() {
		System.out.println("初始化...");
	}

	@MyAfter
	public void destroy() {
		System.out.println("销毁...");
	}

	@MyTest
	public void testSave() {
		System.out.println("save...");
	}

	@MyTest
	public void testDelete() {
		System.out.println("delete...");
	}
}

MyJunitFrameWork

public class MyJunitFrameWork {
	public static void main(String[] args) throws Exception {
		// 1.先找到测试类的字节码:EmployeeDAOTest
		Class clazz = EmployeeDAOTest.class;
		Object obj = clazz.newInstance();
		// 2.获取EmployeeDAOTest类中所有的公共方法
		Method[] methods = clazz.getMethods();
		/* 3.迭代出每一个Method对象
                     判断哪些方法上使用了@MyBefore/@MyAfter/@MyTest注解
                */
		List<Method> mybeforeList = new ArrayList<>();
		List<Method> myAfterList = new ArrayList<>();
		List<Method> myTestList = new ArrayList<>();
		for (Method method : methods) {
			if(method.isAnnotationPresent(MyBefore.class)){
				//存储使用了@MyBefore注解的方法对象
				mybeforeList.add(method);
			}else if(method.isAnnotationPresent(MyTest.class)){
				//存储使用了@MyTest注解的方法对象
				myTestList.add(method);
			}else if(method.isAnnotationPresent(MyAfter.class)){
				//存储使用了@MyAfter注解的方法对象
				myAfterList.add(method);
			}
		}

		// 执行方法测试
		for (Method testMethod : myTestList) {
			// 先执行@MyBefore的方法
			for (Method beforeMethod : mybeforeList) {
				beforeMethod.invoke(obj);
			}
			// 测试方法
			testMethod.invoke(obj);
			// 最后执行@MyAfter的方法
			for (Method afterMethod : myAfterList) {
				afterMethod.invoke(obj);
			}
		}
	}
}

执行结果:

4f9f6df36414a1a85fe37e490e39da5b.png

山寨JPA

要写山寨JPA需要两个技能:注解+反射。

注解已经学过了,反射还有一个进阶内容,之前那篇反射文章里没有提到。至于是什么内容,一两句话说不清楚。慢慢来吧。

首先,要跟大家介绍泛型中几个定义(记住最后一个):

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个ArrayList<E>称为泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

好,接下来看这个问题:

class A<T>{
	public A(){
               /*
                我想在这里获得子类B、C传递的实际类型参数的Class对象
                class java.lang.String/class java.lang.Integer
               */
	}
}

class B extends A<String>{

}

class C extends A<Integer>{

}

我先帮大家排除一个错误答案:直接T.class是错误的。

ee814f4d6372d44b9f256b52b650eec9.png

所以,你还有别的想法吗?

我觉得大部分人可能都想不到,这不是技术水平高低的问题,而是知不知道API的问题。知道就简单,不知道想破脑袋也没辙。

我们先不直接说怎么做,一步步慢慢来。

请先看下面代码:

public class Test {
	public static void main(String[] args) {
		new B();
	}
}

class A<T>{
	public A(){
                //this是谁?A还是B?
		Class clazz = this.getClass();
		System.out.println(clazz.getName());
	}
}

class B extends A<String>{

}

请问,clazz.getName()打印的是A还是B?

答案是:B。因为从头到尾,我们new的是B,这个Demo里至始至终只初始化了一个对象,所以this指向B。

好的,到了这里,我们迈出了第一步:在泛型父类中得到了子类的Class对象!

我们再来分析:

class A<T>{
	public A(){
                //clazz是B.class
		Class clazz = this.getClass();
	}
}

class B extends A<String>{

}

现在我们已经在class A<T>中得到B类的Class对象。而我们想要得到的是父类A<T>中泛型的Class对象。且先不说泛型的Class对象,我们先考虑,如何获得通过子类Class对象获得父类Class对象?

查阅API文档,我们发现有这么个方法:

e963217ee6a35c0298c2838ffd0deb7a.png

Generic Super Class,直译就是“带泛型的父类”。也就是说调用getGenericSuperclass()就会返回泛型父类的Class对象。这非常符合我们的情况。试着打印一下:

7271db24d411aeac8c79cee0313c8efa.png

打印发现,A<T>的Class对象是ParameterizedType的类型的。

ee188900bb2d324da3a1d080f9865c08.png

这里我们不去关心Type、ParameterizedType还有Class之间的继承关系,总之以我们多年的编码经验,子类对象的方法总是更多。所以毫不犹豫地向下转型:

718c642035a71ac5ee5a3bfcb526fc12.png
果然多了好几个方法,还有个getActualTypeArguments(),可以得到泛型参数~

成了!现在我们能在父类中得到子类继承时传来的泛型的Class对象。接下来正式开始编写山寨JPA。

User

package myJPA;

public class User {
	private String name;
	private Integer age;

	public User(String name, Integer age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {

		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}
}

BaseDao<T>

package myJPA;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;

public class BaseDao<T> {

	private static BasicDataSource datasource = new BasicDataSource();

	//静态代码块,设置连接数据库的参数
	static{
		datasource.setDriverClassName("com.mysql.jdbc.Driver");
		datasource.setUrl("jdbc:mysql://localhost:3306/test");
		datasource.setUsername("root");
		datasource.setPassword("123456");
	}

	//得到jdbcTemplate
	private JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
	//泛型参数的Class对象
	private Class<T> beanClass;

	public BaseDao() {
		/*this指代子类
                  通过子类得到子类传给父类的泛型Class对象,假设是User.class
                */
		beanClass = (Class) ((ParameterizedType) this.getClass()
				.getGenericSuperclass())
				.getActualTypeArguments()[0];
	}

	public void add(T bean) {
		//得到User对象的所有字段
		Field[] declaredFields = beanClass.getDeclaredFields();

		//拼接sql语句,表名直接用POJO的类名
                //所以创建表时,请注意写成User,而不是t_user
		String sql = "insert into "
				+ beanClass.getSimpleName() + " values(";
		for (int i = 0; i < declaredFields.length; i++) {
			sql += "?";
			if (i < declaredFields.length - 1) {
				sql += ",";
			}
		}
		sql += ")";

		//获得bean字段的值(要插入的记录)
		ArrayList<Object> paramList = new ArrayList<>();
		for (int i = 0; i < declaredFields.length; i++) {
			try {
				declaredFields[i].setAccessible(true);
				Object o = declaredFields[i].get(bean);
				paramList.add(o);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		int size = paramList.size();
		Object[] params = paramList.toArray(new Object[size]);

		//传入sql语句模板和模板所需的参数,插入User
		int num = jdbcTemplate.update(sql, params);
		System.out.println(num);
	}
}

UserDao

package myJPA;

public class UserDao extends BaseDao<User> {
	@Override
	public void add(User bean) {
		super.add(bean);
	}
}

测试类

package myJPA;

public class TestUserDao {
	public static void main(String[] args) {
		UserDao userDao = new UserDao();
		User user = new User("hst", 20);
		userDao.add(user);
	}
}

测试结果

c5c379150deae15dbf32aaac8da3e4c3.png

a2d2c95a92b022abda0e85110a2bd053.png

细心的朋友肯定已经发现,我的代码实现虽然不够完美,但是最让人蛋疼的还是:要求数据库表名和POJO的类名一致,不能忍...

于是,我决定抄袭一下JPA的思路,给我们的User类加一个Table注解,用来告诉程序这个POJO和数据库哪张表对应:

@Table注解

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
	String value();
}

新的User类(类名加了@Table注解)

package myJPA;

@Table("t_user")
public class User {
	private String name;
	private Integer age;

	public User(String name, Integer age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {

		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

}

新的测试类

package myJPA;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;

public class BaseDao<T> {

	private static BasicDataSource datasource = new BasicDataSource();

	//静态代码块,设置连接数据库的参数
	static{
		datasource.setDriverClassName("com.mysql.jdbc.Driver");
		datasource.setUrl("jdbc:mysql://localhost:3306/test");
		datasource.setUsername("root");
		datasource.setPassword("123456");
	}

	//得到jdbcTemplate
	private JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
	//泛型参数的Class对象
	private Class<T> beanClass;

	public BaseDao() {
		//得到泛型参数的Class对象,假设是User.class
		beanClass = (Class) ((ParameterizedType) this.getClass()
				.getGenericSuperclass())
				.getActualTypeArguments()[0];
	}

	public void add(T bean) {
		//得到User对象的所有字段
		Field[] declaredFields = beanClass.getDeclaredFields();

		//拼接sql语句,【表名从User类Table注解中获取】
		String sql = "insert into "
				+ beanClass.getAnnotation(Table.class).value() 
				+ " values(";
		for (int i = 0; i < declaredFields.length; i++) {
			sql += "?";
			if (i < declaredFields.length - 1) {
				sql += ",";
			}
		}
		sql += ")";

		//获得bean字段的值(要插入的记录)
		ArrayList<Object> paramList = new ArrayList<>();
		for (int i = 0; i < declaredFields.length; i++) {
			try {
				declaredFields[i].setAccessible(true);
				Object o = declaredFields[i].get(bean);
				paramList.add(o);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		int size = paramList.size();
		Object[] params = paramList.toArray(new Object[size]);

		//传入sql语句模板和模板所需的参数,插入User
		int num = jdbcTemplate.update(sql, params);
		System.out.println(num);
	}
}

这下真的是山寨JPA了~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值