Braisdom

Braisdom 查看完整档案

上海编辑  |  填写毕业院校Braisdom  |  自由职业者 编辑 www.objsql.com 编辑
编辑

Braisdom, ObjectiveSql 创始人,主要开发者,有着15+年软件开发&设计经验,精通Java, Ruby, Python 等语言,对面向对象设计,分布式,高并发等系统设计有独立的见解,同时也参与多个在线系统的开发和维护工作。擅长领域:电信、零售、电商等。

个人动态

Braisdom 提出了问题 · 10月23日

关于Java ORM 框架的讨论

目前, Java 领域中有较多ORM 框架,理论基础的差别不大,但实现原理和方法差距较大,希望大家发挥自己的特长,自由讨论

  • Hibernate
  • MyBatis(MyBatis-plus)
  • JOOQ
  • JPA

还有我自己开发的,ObjectiveSQL(https://github.com/braisdom/ObjectiveSql)
中文网站:http://www.objsql.com/

前面的可能大家比较熟悉,希望多了解一下ObjectiveSQL 然后再比较

关注 2 回答 1

Braisdom 发布了文章 · 10月22日

ObjectiveSQL 正式发布 1.3.5 版本

ObjectiveSQL 是一个基于ActiveRecored 模式的Java ORM 框架,零配置,基于已定义的领域模型,自动生成数据库访问逻辑代码,并提供便捷的API,简化Java 编码过程中的SQL 编程,提升应用系统的开发效率,降低代码量。

经过三个月的开发和测试,在10月初发布稳定版本1.3.5,1.3.5 版本主要包括:

  • 动态生成数据模型访问数据库的 JAVA API 代码,其中包括数据库访问的SELECT、INSERT、UPDATE 和DELETE
  • 根据Relation 注解的定义,在查询时自动填充关联对象,同时也避免N+1 查询问题
  • 提供多种方式构造数据模型,主要以Java Bean PropertyDescriptor 形式,也支持Map 形式绑定属性
  • 多数据源支持,只需在DomainModel 中指定数据模型所属的数据源
  • 事务支持,只需要在模型方法中指定Transaction 注解,系统自动生成数据为事务代码
  • 灵活扩展,系统针对业务领域中可能遇见的扩展点提供Interface 和注入接口,主要包括:JDBC 执行器、数据类型转换、SQL 查询、SQL 持久化等

Github:https://github.com/braisdom/ObjectiveSql

中文文档:http://www.objsql.com/docs/basic/objectivesql/

查看原文

赞 0 收藏 0 评论 0

Braisdom 发布了文章 · 10月14日

ObjectiveSQL 扩展之——Redis 缓存

应用系统中针对时效性敏感度比较低的数据,通常会进行缓存,比较流行的缓存系统包括:Redis, Memcache 等,例如:电商中商品的时效敏感度相对较低,商户上线或变更的商品数量和频率相对较大,如果实时变更数据存储,对数据库的冲击比较大;然后,会员对商品变更的的敏感度也有相应的容忍度,这类数据在电商的应用系统中会采取批量存储和查询缓存的策略。ObjectiveSQL 针对数据查询提供了扩展性接口,具体扩展特性如下:

public interface SQLExecutor<T> {

    List<T> query(Connection connection, String sql,
                  TableRowAdapter tableRowAdapter, Object... params) throws SQLException;

    default T insert(Connection connection, String sql,
             TableRowAdapter tableRowAdapter, Object... params) throws SQLException {
        throw new UnsupportedOperationException("The insert is unsupported");
    };

    default int[] insert(Connection connection, String sql,
                 TableRowAdapter tableRowAdapter, Object[][] params) throws SQLException {
        throw new UnsupportedOperationException("The insert is unsupported");
    }

    default int execute(Connection connection, String sql, Object... params) throws SQLException {
        throw new UnsupportedOperationException("The execute is unsupported");
    };
}

SQLExecutor 是ObjectiveSQL 的一个扩展接口,主要的作用有两点:1)针对SQL 的执行过程进行干预,缺省使用的是Apache DBUtils 的形式进行JDBC 操作,主要也就是将关系数据转换成Java Bean,如果如果通过自身高性的的方式进行转换可以实现该接口,并将其注入ObjectiveSQL;2)不改变具体的处理逻辑,但需要将查询出的数据进行缓期或其它形式的处理,也可以实现该接口,但需要extends DefaultSQLExecutor,然后进行个性化处理。

以Redis 缓存为示例,也是比较常用的缓存处理方式,示例如下:

import com.github.braisdom.example.model.Member;
import com.github.braisdom.objsql.DefaultSQLExecutor;
import com.github.braisdom.objsql.TableRowAdapter;
import org.springframework.util.SerializationUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.io.Serializable;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

public class CacheableSQLExecutor<T> extends DefaultSQLExecutor<T> {

    private static final List<Class<? extends Serializable>> CACHEABLE_CLASSES =
            Arrays.asList(new Class[]{Member.class});
    private static final Integer CACHED_OBJECT_EXPIRED = 60;
    private static final String KEY_SHA = "SHA";

    private Jedis jedis = new Jedis("127.0.0.1", 6379);
    private MessageDigest messageDigest;

    public CacheableSQLExecutor() {
        try {
            messageDigest = MessageDigest.getInstance(KEY_SHA);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

    @Override
    public List<T> query(Connection connection, String sql,
                         TableRowAdapter tableRowAdapter, Object... params) throws SQLException {
        Class<?> domainClass = tableRowAdapter.getDomainModelClass();

        if (CACHEABLE_CLASSES.contains(domainClass)) {
            if(!Serializable.class.isAssignableFrom(domainClass))
                throw new IllegalArgumentException(String.format("The %s cannot be serialized"));

            messageDigest.update(sql.getBytes());

            String hashedSqlId = new BigInteger(messageDigest.digest()).toString(64);
            byte[] rawObjects = jedis.get(hashedSqlId.getBytes());

            if (rawObjects != null) {
                return (List<T>) SerializationUtils.deserialize(rawObjects);
            } else {
                List<T> objects = super.query(connection, sql, tableRowAdapter, params);
                byte[] encodedObjects = SerializationUtils.serialize(objects);
                SetParams expiredParams = SetParams.setParams().ex(CACHED_OBJECT_EXPIRED);

                jedis.set(hashedSqlId.getBytes(), encodedObjects, expiredParams);

                return objects;
            }
        }
        return super.query(connection, sql, tableRowAdapter, params);
    }
}

原理很简单,以SQL 为基础,转换为SHA KEY 的形式存储进Redis,并设置过期时间。

查看原文

赞 0 收藏 0 评论 0

Braisdom 发布了文章 · 10月14日

ObjectiveSQL 扩展之—— 自定义Logger Framework

目前,比较流行的Log Framework 有:Apache Commons Logging,Avalon LogKit,log4j,SLF4J 等,ObjectiveSQL 无法确定应用系统使用哪一种 Log Framework,所以提供了扩展接口,由应用系统根据自身的需要进行实现,以Slf4j 为示例,详细介绍应用系统如何扩展实现Log Framework。

ObjectiveSQL 中的扩展接口Logger.javaLoggerFactory.java 如下:

public interface Logger {

    void debug(long elapsedTime, String sql, Object[] params);

    void info(long elapsedTime, String sql, Object[] params);

    void error(String message, Throwable throwable);
}
public interface LoggerFactory {

    Logger create(Class<?> clazz);
}

结合SpringBoot 示例如下:

import com.github.braisdom.objsql.Logger;
import com.github.braisdom.objsql.LoggerFactory;
import com.github.braisdom.objsql.util.StringUtil;

import java.util.Arrays;

public class ObjLoggerFactoryImpl implements LoggerFactory {

    private class ObjLoggerImpl implements Logger {

        private final org.slf4j.Logger logger;

        public ObjLoggerImpl(org.slf4j.Logger logger) {
            this.logger = logger;
        }

        @Override
        public void debug(long elapsedTime, String sql, Object[] params) {
            logger.debug(createLogContent(elapsedTime, sql, params));
        }

        @Override
        public void info(long elapsedTime, String sql, Object[] params) {
            logger.info(createLogContent(elapsedTime, sql, params));
        }

        @Override
        public void error(String message, Throwable throwable) {
            logger.error(message, throwable);
        }

        private String createLogContent(long elapsedTime, String sql, Object[] params) {
            String[] paramStrings = Arrays.stream(params).map(param -> String.valueOf(param)).toArray(String[]::new);
            String paramString = String.join(",", paramStrings);
            return String.format("[%dms] %s, with: [%s]", elapsedTime, sql, String.join(",",
                    paramString.length() > 100 ? StringUtil.truncate(paramString, 99) : paramString));
        }
    }

    @Override
    public Logger create(Class<?> clazz) {
        org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(clazz);
        return new ObjLoggerImpl(logger);
    }
}
LoggerFactory loggerFactory = new ObjLoggerFactoryImpl();
Databases.installLoggerFactory(loggerFactory);

其实,原理很简单,只需要按要求实现Logger 和LoggerFactory 两个接口,在接口中注入自身Log Framework 的实现即可,示例中是以Slf4j 为示例,其它类型的Framework 也类似。完成具体实现后,需要将实例注入ObjectiveSQL。

查看原文

赞 0 收藏 0 评论 0

Braisdom 关注了用户 · 10月13日

GaryAi @victory81

关注 1

Braisdom 关注了用户 · 10月13日

马警 @majing_5f49bf0d9ae55

关注 1

Braisdom 发布了文章 · 10月13日

ObjectiveSQL 源码解读——DomainModelDescriptor

DomainModelDescriptor 设计的初衷是为了适应不同类型的模型定义能够通过ObjectiveSQL 写入数据库或从数据库中读取,在JDK 中也有类似的设计,如PropertyDescriptor,BeanDescriptor 等,其目的也是描述Java Bean 和Property 的基础元信息,只不过在ObjectiveSQL 中DomainModelDescriptor 是一个抽象的定义,描述的是一个能够被ObjectiveSQL 所认识的业务模型,可以是一个Java Bean,也可以是其它对象。

在我过往的项目中,除了常用的Java Bean 模型可以进行数据库的访问,还会存在一些其它类型的模型同时也需要进行数据库存储,具体示例如下:

过程介绍:

  1. 通常数据量过大的情况下会采用Apache Fluent 进行数据缓存,然后集中入库(单批次数据量超过50 万行记录)
  2. 数据需要经过不同系统间传输,也就意味着需要定义传输和存储结构,ProtoBuffer 是比较合适的结构定义,无论是扩展性、压缩和解压效率都很优秀,所以我们选择ProtoBuffer 进行结构定义和数据传输
  3. ProtoBuffer 有自身的数据载体,和普通Java Bean 不一样,如果将数据转换成Java Bean 会产生额外的性能开销,需要经过DomainModelDescriptor 描述后,ObjectiveSQL 才能授受这样的模型

上文描述的是一种 DomainModelDescriptor 的场景,由于不同使用场景下,数据模型的结构也不一样,ObjectiveSQL 充分考虑了场景的变化,所以抽象出DomainModelDescriptor,为数据库访问提供必要的元信息。

查看原文

赞 1 收藏 1 评论 0

Braisdom 发布了文章 · 10月13日

ObjectiveSQL 源码解读——工厂模式

理解设计模式,必须结合具体的案例,单纯的理论学习很难真正掌握设计方法。其实,你真实看透了设计模式后,当发现需要所依赖的逻辑处于一种不稳定的状态时,抽象也就产生了,所谓的设计模式也就是对一些不稳定的依赖形成的一种固有的模式,经过系统的理论整理后也就形成了今天所能见到的设计模式。

工厂模式系列主要是为了封装构建实例的过程,封装的目的主要有两点:1)构造实例的过程比较复杂,而且构造过程会被复用;2)构造过程会根据不同的参数产生不同的实例。ObjectiveSQL 中工厂模式使用的比较多,主要因为使用场景的不确定性比较大,JDBC和SQL 的处理逻辑的灵活性比较大,所以ObjetivSQL 需要充分适应这类灵活性,提供了较多的逻辑的抽象接口。

Query 和Persistence 封装了查询和持久化的逻辑,按ObjectiveSQL 的方式对行为进行抽象。

QueryFactory 和PersistenceFactory 封装了构造抽象的Query 和Persistence,主要因为Query 和Persistence 会在不同的应用系统存在变化,例如:Query 不一定每次都命中数据库,在某些情况下,查询有可能会命中Redis 或其它内存数据库,所以ObjectiveSQL 需要适应这种变化,这样的场景下,工厂模式是最好的选择。ObjectiveSQL 作为抽象型技术框架,针对应用系统中的不同场景提供足够的扩展特性,封装不同应用系统中重复被使用的逻辑,这也就是ObjectiveSQL 存在的价值。

这样的设计只是ObjectiveSQL 设计中基础的一部分,后续会陆续介绍更多的设计方法和理论实践。

查看原文

赞 1 收藏 0 评论 0

Braisdom 关注了用户 · 10月13日

昌维 @changwei

关注 144

Braisdom 赞了问题 · 10月13日

有没有一个轻量级的ORM框架看源码来学习如何设计ORM的?

有没有一个轻量级的ORM框架?就是JAVA的,我想学习一下。不要mybatis那种很大的。个人写的就可以了。

关注 12 回答 8

认证与成就

  • 获得 9 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • ObjectiveSql

    ObjectiveSQL 是一个ORM 框架,以极简的方式让应用系统具备数据库的访问能力。同时,ObjectiveSQL 借鉴了ActiveRecord 模式的设计思想,倡导应用系统中的模型(Model)承担主要的业务逻辑,并封装了基础的SQL 能力,降低模型的重复编码量。

注册于 2019-05-17
个人主页被 235 人浏览