零、前言
实现的功能:
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 {分析过后知道,rowMapper起作用的地方在与mapRow方法内,将ResultSet(数据库记录)转化为user对象。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; } }
于是有了下面的代码:
构建一个方法:传入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容器初始化完成后就会执行该方法。
}
}
|