一个基于注解的orm简单实现:实现思路

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个基于注解的orm简单实现:实现思路相关的知识,希望对你有一定的参考价值。

先来看一段常见的数据库操作代码:

```
	protected User getDataFromDatabase(long id){
		String sql = "select firstname from user where id=?";//1
		Connection conn = DBConnectionFactory.getConnection();
		PreparedStatement stat;
        User user;//2
		try {
			stat = conn.prepareStatement(sql);
			stat.setObject(1, id);
			ResultSet rs = stat.executeQuery();
			user.setFirstName(rs.getString("firstname"));//3
			stat.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return user;
	}
```

在上面代码中存在三个可变部分,如果需要把数据库操作代码修改为任何对象都能使用的同一段代码,则需要提取出这三个可变部分。所以我们修改一下上面代码。
1、首先提取一下sql语句部分。如果我们可以动态生成该sql语句,数据库对象映射器应该就完成了一半。所以我们先来拆解sql语句,看看下面这段代码:
```
	    String selectList="firstname";
        String whereClause="id=?";
        String tableName = "user";
        String sql = "select " + selectList +" from " + tableName +" where " + whereClause;
        protected User getDataFromDatabase(String sql,long id){
		//String sql = "select firstname from user where id=?";//1
		Connection conn = DBConnectionFactory.getConnection();
		PreparedStatement stat;
        User user;//2
		try {
			stat = conn.prepareStatement(sql);
			stat.setObject(1, id);
			ResultSet rs = stat.executeQuery();
			user.setFirstName(rs.getString("firstname"));//3
			stat.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return user;
	}
```
拆解了第一部分,现在看起来好像也感觉差别不大,仅仅是把sql当作参数传递进去。关于第二第三可变部分,我们再来看看下面这段代码:

```
	String selectList="firstname";
    String whereClause="id=?";
    String tableName = "user";
    String sql = "select " + selectList +" from " + tableName +" where " + whereClause;
    Class klass = User.class;
    String fieldName = "firstName";
    String columnName = "firstname";
    Field field = klass.getDeclaredField(fieldName);
    protected Object getDataFromDatabase(String sql,Object id){
	    //String sql = "select id,firstname from user where id=?";//1
		Connection conn = DBConnectionFactory.getConnection();
		PreparedStatement stat;
        Object user;//2
		try {
			stat = conn.prepareStatement(sql);
			stat.setObject(1, id);
			ResultSet rs = stat.executeQuery();
			user = klass.newInstance();
			field.set(user,rs.getObject(columnName));
			//user.setFirstName(rs.getString("firstname"));//3
			stat.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return user;
	}
```
现在再重新看函数体内,与特定user对象的信息,好像没有了,当然这里为了简化问题,user对象只有一个firstname,后面会看到有多个变量时,仅仅是field.set()那里变成了一个for循环。然后我们再来优化一下代码,毕竟所有代码堆在一个地方乱七八糟,非常不容易理解。而且如果代码仅仅是这样的话,也没有任何的可重用性。我们先把函数体提取到BaseMapper.class。把其他可变的部分,提取到TableMap.class和OneToOneColumnMap.class。TableMap存储对象与数据库表映射信息,OneToOneColumnMap存储对象field与表列的映射信息。每一个对象都会有一个BaseMapper实例,存储各自的对象关系映射信息。代码如下:
```
class BassMapper{
     protected TableMap<T> tableMap;
     public Object getDataFromDatabase(String sql,Object... param){
		Connection conn = DBConnectionFactory.getConnection();
		PreparedStatement stat;
		Object result = null;
		try {
			stat = conn.prepareStatement(sql);
			for(int i = 0;i < param.length;i++){
				stat.setObject(i + 1, param[i]);
			}
			ResultSet rs = stat.executeQuery();
			if(rs.next()){
				result = (T) tableMap.getKlass().newInstance();
				for(Iterator<OneToOneColumnMap> it = tableMap.getOneToOneColumns();it.hasNext();){
					OneToOneColumnMap columnMap = it.next();
					Object columnValue = rs.getObject(columnMap.getColumnName());
					columnMap.setField(result, columnValue);
				}
			}
			stat.close();
		} catch (SQLException | InstantiationException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return result;
	}
}
```

```
class OneToOneColumnMap{
    private String columnName;
	private String fieldName;
	protected Field field;
	private TableMap dataMap;
	
	public OneToOneColumnMap(String columnName,String fieldName,TableMap dataMap){
		this.columnName = columnName;
		this.fieldName = fieldName;
		this.dataMap = dataMap;
		initField();
	}
	
	public String getColumnName(){
		return this.columnName;
	}
	
	public String getFieldName(){
		return this.fieldName;
	}
	
	public Object getValue(Object subject){
		try {
			return field.get(subject);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public void setField(Object result,Object columnValue){
		try {
			field.set(result, columnValue);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	protected void initField(){
		try {
			this.field = dataMap.getKlass().getDeclaredField(getFieldName());
			field.setAccessible(true);
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
```

```
class TableMap{
    private Class<T> domainClass;
	private String tableName;
	private OneToOneColumnMap primaryKeyColumn;  
	private List<OneToOneColumnMap> oneToOneColumnMaps = new ArrayList<OneToOneColumnMap>();
    
    public TableMap(String tableName,Class<T> domainClass){
		this.domainClass = domainClass;
		this.tableName = tableName;
	}
    public void addOneToOneColumn(String columnName,String fieldName){
		OneToOneColumnMap columnMap = new OneToOneColumnMap(columnName,fieldName,this);
		if(!oneToOneColumnMaps.contains(columnMap)){
			if(primaryKeyColumn == null){
				primaryKeyColumn = columnMap;
			}
			oneToOneColumnMaps.add(columnMap);
		}
	}

	public void setPrimaryKeyColumn(String columnName, String fieldName){
		this.primaryKeyColumn = new OneToOneColumnMap(columnName,fieldName,this);
		if(!oneToOneColumnMaps.contains(primaryKeyColumn)){
			oneToOneColumnMaps.add(primaryKeyColumn);
		}
	}

	public String primaryKeyWhereClause(){
		return primaryKeyColumn.getColumnName() + " = ? ";
	}
	
	public Object primaryKeyColumnName(){
		return primaryKeyColumn.getColumnName();
	}
	
	public Object primaryKeyValue(Object domainObject){
		return primaryKeyColumn.getValue(domainObject);
	}
	
	public String getTableName(){
		return this.tableName;
	}
	
	public String insertList(){
		StringBuffer result = new StringBuffer("?");
		for(int i = 0;i < oneToOneColumnMaps.size() - 1;i++){
			result.append(",");
			result.append("?");
		}
		return result.toString();
	}
	
	public String columnList(){
		StringBuffer result = new StringBuffer(" ");
		for(Iterator<OneToOneColumnMap> it = getOneToOneColumns();it.hasNext();){
			OneToOneColumnMap columnMap = it.next();
			result.append(columnMap.getColumnName());
			result.append(",");
		}
		result.setLength(result.length() - 1);
		return result.toString();
	}
	
	public String updateList(){
		StringBuffer result = new StringBuffer(" SET ");
		for(Iterator<OneToOneColumnMap> it = getOneToOneColumns();it.hasNext();){
			OneToOneColumnMap column = it.next();
			result.append(column.getColumnName());
			result.append("=?,");
		}
		result.setLength(result.length() - 1);
		return result.toString();
	}
	
	public String getColumnForField(String fieldName){
		for(Iterator<OneToOneColumnMap> it = getOneToOneColumns();it.hasNext();){
			OneToOneColumnMap columnMap = it.next();
			if(columnMap.getFieldName().equals(fieldName)){
				return columnMap.getColumnName();
			}
		}
		return null;
	}
}
```
BaseMapper通过一个TableMap来初始化,依赖关系如下:
![这里写图片描述](http://img.blog.csdn.net/20170211104536444?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaHV4dWVtaW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
项目代码:https://github.com/hu-xuemin/xBlog.git

以上是关于一个基于注解的orm简单实现:实现思路的主要内容,如果未能解决你的问题,请参考以下文章

hibernate用注解的方式实现orm

使用自定义注解简单实习orm框架的sql生成

自定义注解实现简单的orm映射框架

基于SpringBoot实现一个简单的权限控制注解

一个简单的MVC框架的实现-基于注解的实现

hibernate用配置文件的方式实现orm