[JAVA]打造一个缩水版的ORM映射(mih)-- beta

零、前言

实现的功能:

1.数据库record与javaBean的互相映射

2.抽象save、update、saveOrUpdata、findById、delete方法

3.基于注解的配置


一、基于注解的配置

Java自定义注解: http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html (看完这篇文章需要20分钟)

下面是代码:

@Target({ElementType.METHOD,ElementType.FIELD})//@Target的意思是这个注解可以被用在哪里,这里的范围是 method filed也就是方法和属性上都可以注解我们定义的@Column
@Retention(RetentionPolicy.RUNTIME)//@Retention意思是注解的生命周期为Runtime有效
public @interface Column {//除了红字外都是关键词,没什么好说的
	String name() default "" ;//注解时可以传进来的属性,稍后结合例子看就懂了
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
	String name() default "";
}
可以看到注解的定义跟类与接口是类似的。这里定义了两个注解,一个是Column用来注解在属性(字段名)上。一个是Table用来注解在类上(表名)。分别有name作为注解的传递参数(默认为""字符串)。
定义好了注解,下面我们开始使用注解(这个例子是不全的 代码):
@Table(name = "t_user_info")//括号内的name就是我们在定义注解时写的String name() defalut ""; 也可以定义传入其他类型
public class UserInfo extends BaseEntity {
	@Column(name = "company_id")
	private Long supplierInfo; // 所属公司
		
	@Column(name = "office_phone")
	private String officePhone; // 办公电话
	
	@Column(name = "sex")
	private Long sex; // 性别 1-男 0-女	

	public String getOfficePhone() {
		return officePhone;
	}
	public void setOfficePhone(String officePhone) {
		this.officePhone = officePhone;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}
二、数据库record与javaBean的互相映射
由于兼容问题,数据库record字段与javaBean中属性的映射,依靠的是注解上的name与该注解依赖的属性名而绑定。就像刚才我们定义的UserInfo(红字处),依赖注解中的name与绑定字段的名称而绑定。
为了读取这种绑定关系,我们需要使用java的反射技术。因为网上比较多资料,这里不提供链接了。(待会代码上会有对应的注释)
有人认为反射降低了性能,我觉得这是可以规避的,比如在性能瓶颈处增加反射的缓存处理。但暂时来看,我们的项目没有这个性能问题。
下面的解释基于 UserInfo
1)反射的基础
如何获取目标Class类
Class cl = UserInfo.class; //A
Class cl = Class.forName("com.test.UserInfo");//B
UserInfo us = new UserInfo();//C
Class cl = us.getClass();
有如上ABC三种方法获取,除了C方法外,其他两个都会抛出异常(classNotFound)。
Class类有什么用
Field[] fs = cl. getDeclaredFields ();//获取该类的所有属性包括private protected等都会被获取
//同理还有其他类的内容可以被获取,比如Method等。但这里我们不需要,所以就不写出来了。
Field类有什么用
向对象写入数据
假如有个Field field = cl.getDeclaredFiled("name");//我们要设置name为 ‘ 张三’
Object obj = cl.newInstance();//相当于调用new UserInfo();无参构造函数。也可以调用有参的构造方法,不过要用到Constructor对象,这里不写出来了。
field.set(obj,“张三”);//field还有很多方法,但我觉得set方法最实用,其他可以再搜索jdk查看
在对象读取数据
比如有个obj,我们要从中获取name属性的值,怎么做呢···
正常情况是:us.getName();//使用getter
然而现在情况是,Object obj = //from DB
Field field = cl.getDeclaredFiled("name");
String name = (String)field.get(obj);//返回值就是了
2)Spring  RowMapper
Spring RowMapper可以实现将一条记录包装成一个javaBean。
下面是普通用法,我们要通过反射构造一个类似下面的rowMapper就成功了。resultSet有许多方法getString getInt,这里我都不用,而是选择getObject。
protected class ItemMapper implements RowMapper<UserInfo> {  
  public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException {  
   UserInfo user= new UserInfo();  
user.setId(rs.getInt("id")); user.setUserId(rs.getInt("user_id")); user.setName(rs.getString("name")); user.setPhone(rs.getString("phone")); user.setEmail(rs.getString("email")); return user; } }
分析过后知道,rowMapper起作用的地方在与mapRow方法内,将ResultSet(数据库记录)转化为user对象。
于是有了下面的代码:
构建一个方法:传入ResultSet 返回一个Object
public Object buildBean(ResultSet row) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException {
		Object obj = cl.newInstance();
		Field[] fields = cl.getDeclaredFields();
		Field[] fatherFields = cl.getSuperclass().getDeclaredFields();
		for (int i = 0; i < fatherFields.length; i++) {
			if(Modifier.isProtected(fatherFields[i].getModifiers()) || Modifier.isPublic(fatherFields[i].getModifiers())){
				fatherFields[i].setAccessible(true);
				Annotation[] anos = fatherFields[i].getAnnotations();
				if(anos.length > 0){
					Column ano = (Column) anos[0];
					fatherFields[i].set(obj, row.getObject(ano.name()));
				}
			}
		}
		for (int i = 0; i < fields.length; i++) {
			fields[i].setAccessible(true);
			Annotation[] anos = fields[i].getAnnotations();
			if(anos.length > 0){
				Column ano = (Column) anos[0];
				fields[i].set(obj, row.getObject(ano.name()));
			}
		}
		return obj;
	}
这就完成了 将数据库的record转化为javaBean
而将javaBean转化为record,其实就是针对javaBean的save update 操作,就在第三步。。
三、抽象的save、update等
下面是项目代码:
构建 update的sql语句
        StringBuilder sb = new StringBuilder("UPDATE ");
	sb.append(tableName);
	sb.append(" SET ");
	Field[] fatherFields = cl.getSuperclass().getDeclaredFields();//这里从是父类的继承的属性
	for (int i = 0; i < fatherFields.length; i++) {
		if(Modifier.isProtected(fatherFields[i].getModifiers()) || Modifier.isPublic(fatherFields[i].getModifiers())){
			fatherFields[i].setAccessible(true);
			Annotation[] anos = fatherFields[i].getAnnotations();//该属性是否有Column注解
			if(anos.length > 0){
				Column ano = (Column) anos[0];//拿到注解中的name,也就是数据库表中的字段名
				sb.append(ano.name());
				sb.append(" = ?,");
			}
		}
	}
	Field[] fields = cl.getDeclaredFields();
	for (int i = 0; i < fields.length; i++) {
		Annotation[] anos = fields[i].getAnnotations();
		if(anos.length > 0){
			Column ano = (Column) anos[0];
			sb.append(ano.name());
			sb.append(" = ?,");
		}
	}
	sb.delete(sb.length()-1, sb.length());
	sb.append(" where Id = ?");
	updateSQL = sb.toString();
针对 UserInfo来说,结果大致如下:
updateSQL:update t_user_info set company_id = ?,sex =?,name =? where id =?
执行updataSQL的过程大概如下:
        if(obj.getClass() == cl){
	ArrayList<Object> args = new ArrayList<Object>();
	//父类的字段
	Field[] fatherFields = cl.getSuperclass().getDeclaredFields();
	for (int i = 0; i < fatherFields.length; i++) {
		if(Modifier.isProtected(fatherFields[i].getModifiers()) || Modifier.isPublic(fatherFields[i].getModifiers())){
			fatherFields[i].setAccessible(true);
			Annotation[] anos = fatherFields[i].getAnnotations();
			if(anos.length > 0){
				args.add(fatherFields[i].get(obj));
			}
		}
	}
	//本类字段
	Field[] fs = cl.getDeclaredFields();
	for (int i = 0; i < fs.length; i++) {
		fs[i].setAccessible(true);
		Annotation[] anos = fs[i].getAnnotations();
		if(anos.length > 0){
			args.add(fs[i].get(obj));
		}
	}
	//id
	Field idF = cl.getSuperclass().getDeclaredField("id");
	idF.setAccessible(true);
	args.add(idF.get(obj));
			
	//jdbc执行updateSQL+args
	jdbcTemplate.update(updateSQL, args.toArray());
	    return obj;
	}else{
	    throw new IllegalArgumentException("非法的bean类型");
	}




在Spring的web项目中,我们可以介入Spring的启动过程。我们希望在Spring容器将所有的Bean都初始化完成之后,做一些操作,这个时候我们就可以实现一个接口:

1
2
3
4
5
6
7
package  com.yk.test.executor.processor
public  class  InstantiationTracingBeanPostProcessor  implements  ApplicationListener<ContextRefreshedEvent> {
     @Override
     public  void  onApplicationEvent(ContextRefreshedEvent event) {
       //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值