前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【springboot】基于jdbctemplate封装一个轻量级orm框架

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

作者头像
code2roc
发布2023-07-19 14:32:56
2680
发布2023-07-19 14:32:56
举报

最近一直在使用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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档