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

来源:互联网 发布:掌盟数据拉取失败 编辑:程序博客网 时间:2024/04/28 13:20

零、前言

实现的功能:

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; //AClass cl = Class.forName("com.test.UserInfo");//BUserInfo us = new UserInfo();//CClass 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));}}//idField idF = cl.getSuperclass().getDeclaredField("id");idF.setAccessible(true);args.add(idF.get(obj));//jdbc执行updateSQL+argsjdbcTemplate.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容器初始化完成后就会执行该方法。
 }
}
0 0
原创粉丝点击