【springboot】基于jdbctemplate封装一个轻量级orm框架

2023-07-19 14:32:56 浏览数 (1)

最近一直在使用springboot做后台系统,之前数据操作框架选型hibernate太重,mybaits又是半自动化的,怎么都感觉用着不方便,所以先用jdbctemplate快速灵活的实现业务功能,但是后来发现还是需要封装一套数据操作的基础框架,使开发更加高效,免除大量的重复劳动,jdbctemplate灵活快速并且封装好了一些基础功能,我们要做的就是对数据进行内部映射,对外提供方法接口,使得开发更加快捷高效,话不多说,下面就来具体介绍一下。

项目地址:FastORM_Java: 基于jdbctemplate封装轻量级ORM操作框架

maven引用:

代码语言:javascript复制
<dependency>
  <groupId>com.gitee.grassprogramming</groupId>
  <artifactId>fastorm</artifactId>
  <version>0.0.2</version>
</dependency>

如果想了解发布自己的组件到maven中央仓库可以参考:发布maven项目至中央仓库终极教程与疑难问题汇总解决帖_月月鸟先森的博客-CSDN博客

orm框架基于jdbctemplate做基础查询,transactiontmplate事物处理,内部采用实体类扫描做数据映射缓存,对外提供以下一些功能

1:实体类型的数据库注解

TableName指定实体类对应的表明,未指定默认类名,Key指定表唯一键,Ignore指定类中不需要映射的字段

2:基于实体类的基础的增删改查

代码语言:javascript复制
        //增
        Frame_Config frame_config = new Frame_Config();
        frame_config.setConfigGuid(UUID.randomUUID().toString());
        frame_config.setConfigName("test1111111");
        frame_config.setConfigValue("2222222");
        frame_config.setConfiIntro("hahahahhah");
        frame_config.setAddDate(new Date());
        dbUtil.insert(frame_config);
        //删
        dbUtil.delete(frame_config);
        //改
        frame_config.ConfigValue="222";
        dbUtil.update(frame_config);
        //查
        Frame_Config frame_config = dbUtil.findOne("8c0648f4-c3eb-4447-a0ff-8a63c41ece3b", Frame_Config.class);

3:分页查找与多表联查

代码语言:javascript复制
//普通列表查找 
list = dbUtil.findPage("*","",null,"AddDate desc",1,1,Frame_Config.class);
list = dbUtil.findList("ConfigName","ConfigGuid=?",new Object[]{"8c0648f4-c3eb-4447-a0ff-8a63c41ece3b"},"AddDate desc",Frame_Config.class);
//分页查找
list = dbUtil.findPage("*","",null,"AddDate desc",1,1,Frame_Config.class);
//多表联查
List<Map<String,Object>> viewlist = dbUtil.findView("frame_user a left join frame_userextend b on a.userguid=b.userguid","b.Email","a.userguid=?",new Object[]{"e7c8f780-ae53-4cd0-b0a7-0a857132f7fe"},"a.userguid desc");

4:批量插入

代码语言:javascript复制
       List<Frame_Config> batchlist = new ArrayList<>();
        for (int i=0;i<10;i  ){
            Frame_Config f = new Frame_Config();
            f.setConfigGuid(UUID.randomUUID().toString());
            f.setConfigName("test1111111");
            f.setConfigValue("2222222");
            f.setConfiIntro("hahahahhah");
            f.setAddDate(new Date());
            batchlist.add(f);
        }
        dbUtil.insertBatch(batchlist);

5:基于标识主键或其他唯一键的单条记录对象查找

代码语言:javascript复制
 public <T extends BaseEntity> T findOne(String keyName,String keyGuid, Class<T> tClass)

6:直接执行sql语句

代码语言:javascript复制
 dbUtil.executeSQL("update frame_userextend set Email=? where userguid=?",new Object[]{"aaaaaaaa","d33ea591-93d4-4f74-bc9e-392db05995c0"});

7:获取执行结果操作(executescar)

代码语言:javascript复制
String loginid = dbUtil.executeSQLToString("select loginid from frame_user where userguid=?",new Object[]{"e7c8f780-ae53-4cd0-b0a7-0a857132f7fe"});

8:每个数据操作方法都提供指定数据源的操作重载

代码语言:javascript复制
        //测试单个操作指定的库
        DriverManagerDataSource source =     
        dbUtil.getDataSource("jdbc:mysql://127.0.0.1:3306/orm? 
        useUnicode=true&characterEncoding=UTF- 
        8&allowMultiQueries=true","root","11111","com.mysql.jdbc.Driver");
        Frame_User frame_user = dbUtil.findOne("3333", Frame_User.class,source);

9.提供委托形式的事务操作(事物中的操作需要使用同一数据源)

代码语言:javascript复制
DriverManagerDataSource transitionDataSource = dbUtil.getTransitionDataSource();
        dbUtil.executeTransition(transitionDataSource, new TransitionAction() {
            @Override
            public void doTransitionOption(DriverManagerDataSource dataSource){
               try {
                   Frame_Config frame_config = new Frame_Config();
                   frame_config.setConfigGuid(UUID.randomUUID().toString());
                   frame_config.setConfigName("test1111111");
                   frame_config.setConfigValue("2222222");
                   frame_config.setConfiIntro("hahahahhah");
                   frame_config.setAddDate(new Date());
                   dbUtil.insert(frame_config,dataSource);
                   throw new RuntimeException();
               }
               catch (Exception e){
                   e.printStackTrace();
                   throw new RuntimeException();
               }
            }
        });

10:提供全局的SQL语句跟踪

代码语言:javascript复制
        //测试SQL日志追踪
        dbUtil.OpenTrace();;
        frame_config = dbUtil.findOne("8c0648f4-c3eb-4447-a0ff-8a63c41ece3b", 
        Frame_Config.class);
        dbUtil.CloseTrace();

功能大概就是这么多,更多详细请查看项目ReadMe,接下来就说以下开发中一些功能或者问题的解决

1.如何实现SQL监控

可能如果简单来做就是在底层的jdbcteplate的每个执行方法中加入手动日志,但是这样不利于后续扩展,所以就采用了cglib中的动态代理,我有一个Command执行类,我就不使用new的方式,而是使用代理方式进行初始化,就可以对类的执行方法进行拦截

代码语言:javascript复制
public class CommandFactory {
    public static Command getCommand(){
        CommandProxy commandProxy = new CommandProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Command.class);
        enhancer.setCallback(commandProxy);
        Command command = (Command)enhancer.create();
        command.setJdbcTemplate(Config.GetInstance().getJdbcTemplate());
        return command;
    }

}

public class CommandProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //追踪指定的方法
        if(method.isAnnotationPresent(TraceMethod.class)){
            Command command = (Command)o;
            //开启sql跟踪,打印sql语句及参数
            if(command.isTraceSQL()){
                LogUtil.writeLog("=========================================================");
                LogUtil.writeLog("SQL语句:" command.getSQLText());
            }
        }
        return methodProxy.invokeSuper(o, objects);
    }
}

2.如何进行反射效率优化

反射其实效率已经很快了,我们这里做的就是对一些重复操作进行优化,最重要的就是实体类字段和类名的缓存,我们首先要做的就是在项目启动时进行扫描,获取所有继承我们定义的基类BaseEntity的类,进行类数据缓存,扫描类我这里就不贴了,在项目util目录下的ScanUtil,就看下初始化的方法,后续所有的Mapper操作就都从缓存中取就可以了

代码语言:javascript复制
    public static  void initClassCache(Class runClass) throws  Exception{
        if(null==cacheMap){
            cacheMap = new HashMap<Class,ClassCache>();
        }else{
            cacheMap.clear();
        }

        List<Class> classfilter = new ArrayList<Class>();
        classfilter.add(BaseEntity.class);
        classfilter.add(BaseExtendEntity.class);
        List<Class<?>> clsList = ScanUtil.getAllClassByPackageName(runClass.getPackage(),classfilter);
        //进行所有class类的缓存
        for(Class item:clsList){
            ClassCache classCache = new ClassCache();
            String tableName = InitTableName(item);
            Field keyField = initKeyField(item);
            String keyFieldName = "";
            if(null!=keyField){
                keyFieldName = keyField.getName();
            }
            List<Field> fs = Arrays.asList(item.getDeclaredFields());
            //去除标记Ignore的字段不映射
            fs = fs.stream().filter(((Field f)-> !f.isAnnotationPresent(Ignore.class))).collect(Collectors.toList());
            classCache.setTableName(tableName);
            classCache.setKeyField(keyField);
            classCache.setKeyFieldName(keyFieldName);
            classCache.setFields(fs);
            cacheMap.put(item,classCache);
        }
        System.out.println("初始化ORM缓存成功,共缓存" cacheMap.size() "个class");
    }

说明:本项目适用于springboot,mysql数据库,未经过严格测试,学习型项目,还希望大家多提意见,共同进步,如果可以,请给个Starspi

0 人点赞