您的位置:首页 > 编程语言 > Java开发

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

2016-01-13 16:06 453 查看
零、前言

实现的功能:

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容器初始化完成后就会执行该方法。

 
}

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 架构 orm 数据库