Bootstrap

分布式专题-分布式缓存技术之MongoDB03-手写基于MongoDB的ORM框架

前言

前面的章节,关于分布式缓存技术,我们分析了《分布式缓存技术之Redis的使用以及原理》、从这一节开始,继续来说说MongoDB。

关于MongoDB,一共五小节内容,分别是:

Java操作MongoDB的API介绍

首先要导入pom依赖:

		<!-- mongo官方推荐的一个ORM框架 -->
		<dependency>
			<groupId>org.mongodb.morphia</groupId>
			<artifactId>morphia</artifactId>
			<version>1.3.2</version>
		</dependency>

NativeCRUD

使用原生的mongoClient向test-demo数据库插入Document 类型的一条数据:

   public static void main(String[] args) {
        MongoClient mongoClient = MongoClients.create("mongodb://192.168.200.111:27017");

        MongoDatabase db = mongoClient.getDatabase("test-demo");

        MongoCollection coll = db.getCollection("t_member");


        Document doc = new Document("name", "MongoDB")
                .append("type", "database")
                .append("count", 1)
                .append("versions", Arrays.asList("v3.2", "v3.0", "v2.6"))
                .append("info", new Document("x", 203).append("y", 102));

        coll.insertOne(doc);

//        System.out.println(mongoClient);
    }

效果展示:
在这里插入图片描述
成功插入

MongoCRUD

使用DBCollection 向test-demo数据库插入DBObject 类型的一条数据:

   public static void main(String[] args) {

        Mongo mongo = new Mongo("192.168.200.111",27017);

        DB db = new DB(mongo,"test-demo");

        DBCollection collection =  db.getCollection("member");

        //类比法:JDBC,相对来说比较底层
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name","Tom");
        dbObject.put("age",18);
        dbObject.put("addr","HunanChangsha");

        collection.insert(dbObject);

        DBCursor cursor = collection.find();
        for (DBObject obj : cursor){
            System.out.println(obj);
        }
    }

效果展示:

在这里插入图片描述
插入成功

Morphia

使用Morphia向test-demo数据库插入Member 类型的一条数据:

 public static void main(String[] args) {

        //吗啡
        final Morphia morphia = new Morphia();
        Datastore ds = morphia.createDatastore(new MongoClient("192.168.200.111",27017),"test-demo");

        Member member = new Member();
        member.setName("Tom");
        member.setAge(18);
        member.setAddr("HunanChangsha");

        Key<Member> key = ds.save(member);

        System.out.println(key.getId());
    }

Member

public class Member {


    @Id
    private ObjectId  id;
    private String name;
    private int age;
    private String addr;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

效果展示:
在这里插入图片描述
插入成功

手写实现基于MongoDB的ORM框架

手写ORM的意义

1、结合业务场景,需要手写,解放 双手。
2、主要讲实现思想及原理。
3、更好地监控、统一管理和维护(可控性更强)。

手写基于MongoDB的ORM框架

我们在前面的章节讲到了基于手写jdbc的orm框架,那个是为了mysql数据库准备的,这里我们基于MongoDB数据库来手写orm框架,功能上类似于mybatis-plus(在mybatis的基础上进一步封装,让使用者面向对象操作mysql数据库~)。

本节只截取部分核心代码,全部代码请移步至文末后记部分,有gitlhub分享地址,欢迎访问~
完整代码目录结构:
在这里插入图片描述

流程梳理

因为我用的是springboot项目,所以直接在项目引入mongodb:
在这里插入图片描述
关于工具类:

  • BeanUtils:扩展Apache Commons BeanUtils, 提供一些反射方面缺失功能的封装.
  • DataUtils:提供各种对数据进行处理的方法
  • GenericsUtils:泛型操作类
  • ObjectUtils:对象工具类
  • StringUtils:String工具类

关于核心类BaseDaoSupport,实际上是对于mongoTemplate进一步封装

public abstract class BaseDaoSupport<T extends Serializable,PK extends Serializable> {


    private MongoTemplate mongoTemplate;
    private EntityOperation<T> op;

    public BaseDaoSupport(){
        Class<T> entityClass = GenericsUtils.getSuperClassGenricType(getClass(),0);
        op = new EntityOperation<T>(entityClass);
    }

    protected void setTempate(MongoTemplate tempate){
        this.mongoTemplate = tempate;
    }

    protected abstract String getPKColumn();


    //可控
    protected List<T> find(QueryRule queryRule){
       // "starttime " + "" + System.currentTimeMillis();
        QueryRuleBulider bulider = new QueryRuleBulider(queryRule);
        Query query = bulider.getQuery();

        return mongoTemplate.find(query,op.entityClass);
    }


    protected int saveAll(List<T> list){
        mongoTemplate.insertAll(list);
        return list.size();
    }


    protected  T get(PK id){
        QueryRule queryRule = QueryRule.getInstance();
        queryRule.andEqual(this.getPKColumn(),id);
        QueryRuleBulider bulider = new QueryRuleBulider(queryRule);
        Query query = bulider.getQuery();

        return mongoTemplate.findOne(query,op.entityClass);
    }

    protected int delete(T entity){
        return mongoTemplate.remove(entity).getN();
    }

}

CustomConfig 加载自定义配置:

public class CustomConfig extends PropertyPlaceholderConfigurer{
	
	private final String PLACEHOLDER_START = "${";
	
	private static Map<String, String> ctx; 
	
	@Override  
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,Properties props) throws BeansException {  
		resolvePlaceHolders(props);
        super.processProperties(beanFactoryToProcess, props);  
        ctx = new HashMap<String, String>();  
        for (Object key : props.keySet()) {  
            String keyStr = key.toString();  
            String value = props.getProperty(keyStr);  
            ctx.put(keyStr, value);  
        }
    }  
  
	
	/**
	 * 获取已加载的配置信息
	 * @param key
	 * @return
	 */
    public static String getValue(String key) {  
        return ctx.get(key);
    }
	
	
	/**
	 * 获取已加载的配置信息
	 * @param key
	 * @return
	 */
    public static String getString(String key) {  
        return ctx.get(key);
    }
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static int getInt(String key) {  
        return Integer.valueOf(ctx.get(key));
    }
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static boolean getBoolean(String key) {  
        return Boolean.valueOf(ctx.get(key));
    }
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static long getLong(String key) {  
        return Long.valueOf(ctx.get(key));
    }
    
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static short getShort(String key) {  
        return Short.valueOf(ctx.get(key));
    }
    
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static float getFloat(String key) {  
        return Float.valueOf(ctx.get(key));
    }
    
    
    /**
     * 获取已加载的配置信息
     * @param key
     * @return
     */
    public static double getDouble(String key) {  
        return Double.valueOf(ctx.get(key));
    }
    
    /**
     * 获取所有的key值
     * @return
     */
    public static Set<String> getKeys(){
    	return ctx.keySet();
    }
    
    
    /** 
     * 解析占位符 
     * @param properties 
     */  
    private void resolvePlaceHolders(Properties properties) {  
        Iterator itr = properties.entrySet().iterator();  
        while ( itr.hasNext() ) {  
            final Map.Entry entry = ( Map.Entry ) itr.next();  
            final Object value = entry.getValue();  
            if ( value != null && String.class.isInstance( value ) ) {  
                final String resolved = resolvePlaceHolder(properties, (String)value );  
                if ( !value.equals( resolved ) ) {  
                    if ( resolved == null ) {  
                        itr.remove();  
                    }  
                    else {  
                        entry.setValue( resolved );  
                    }  
                }  
            }  
        }  
    }  
      
    /** 
     * 解析占位符具体操作 
     * @param prots
     * @param value
     * @return 
     */  
    private String resolvePlaceHolder(Properties prots,String value) {  
        if ( value.indexOf( PLACEHOLDER_START ) < 0 ) {  
            return value;  
        }  
        StringBuffer buff = new StringBuffer();  
        char[] chars = value.toCharArray();  
        for ( int pos = 0; pos < chars.length; pos++ ) {  
            if ( chars[pos] == '$' ) {  
                if ( chars[pos+1] == '{' ) {  
                    String key = "";  
                    int x = pos + 2;  
                    for (  ; x < chars.length && chars[x] != '}'; x++ ) {  
                    	key += chars[x];  
                        if ( x == chars.length - 1 ) {  
                            throw new IllegalArgumentException( "unmatched placeholder start [" + value + "]" );  
                        }  
                    }  
                    String val = extractFromSystem(prots, key);  
                    buff.append( val == null ? "" : val );  
                    pos = x + 1;  
                    if ( pos >= chars.length ) {  
                        break;  
                    }  
                }  
            }  
            buff.append( chars[pos] );  
        }  
        String rtn = buff.toString();  
        return isEmpty( rtn ) ? null : rtn;  
    } 
      

    /** 
     * 获得系统属性 当然 你可以选择从别的地方获取值 
     * @param prots
     * @param key
     * @return 
     */  
    private String extractFromSystem(Properties prots,String key) {  
        try {  
            return prots.getProperty(key);  
        }  
        catch( Throwable t ) {  
            return null;  
        }
    }  
      
    /** 
     * 判断字符串的空(null或者.length=0) 
     * @param string 
     * @return 
     */  
    private boolean isEmpty(String string) {  
        return string == null || string.length() == 0;  
    }
}

核心类:QueryRule 可以参考mybatis-plus

public final class QueryRule implements Serializable {
	private static final long serialVersionUID = 1L;
	public static final int ASC_ORDER = 101;
	public static final int DESC_ORDER = 102;
	
	private List<Rule> ruleList = new ArrayList<Rule>();
	private List<QueryRule> queryRuleList = new ArrayList<QueryRule>();
	private String propertyName;

	private QueryRule() {}

	private QueryRule(String propertyName) {
		this.propertyName = propertyName;
	}

	public static QueryRule getInstance() {
		return new QueryRule();
	}
	
	/**
	 * 添加升序规则
	 * @param propertyName
	 * @return
	 */
	public QueryRule addAscOrder(String propertyName) {
		this.ruleList.add(new Rule(ASC_ORDER, propertyName));
		return this;
	}

	/**
	 * 添加降序规则
	 * @param propertyName
	 * @return
	 */
	public QueryRule addDescOrder(String propertyName) {
		this.ruleList.add(new Rule(DESC_ORDER, propertyName));
		return this;
	}
	
//更多规则,这里不做截图,完整代码直接看github地址即可
}
ORM代码测试

我们以insertAll和selectAll做一个简单的测试:

  @Test
    public void testInsertAll(){

        List<Member> data = new ArrayList<Member>();
        data.add(new Member("tom","123456",1,18));
        memberDao.insertAll(data);

    }


    @Test
    public void testSelect(){
        QueryRule queryRule = QueryRule.getInstance();
        queryRule.andEqual("nickname","tom");
        List<Member> memberList = memberDao.select(queryRule);

        System.out.println(JSON.toJSONString(memberList,true));

    }

测试结果:
在这里插入图片描述

插入成功~
看看查询结果:
在这里插入图片描述
查询成功~
ORM的CRUD其他方法,读者有兴趣可自行从github下载使用

后记

java操作mongodb的api代码:
https://github.com/harrypottry/mongo-morphia

手写基于mongodb的ORM框架代码:
https://github.com/harrypottry/mongo-orm

更多架构知识,欢迎关注本套Java系列文章
Java架构师成长之路

;