turbo一个基于Sping JDBC封装的ORM框架
ccwgpt 2024-09-17 12:28 35 浏览 0 评论
1、受不了hibernate,MyBatis太多的xml配置,就想能不配置就不配置。这种思想也一直延续到自己做产品,简单就是美。
2、由于自己曾经是orale的dba,所以就自己控制底层拼装sql,为未来的优化留后门,不想啥都让hibernate和mybatis做了,还是透明的方式,无法在底层插入自己的优化手段。
3、想用对象查询用对象查询,想写sql写sql,想混合就混合。
4、为了适应自己的前端框架,使得mvc各层对接更加轻量级,让各种什么PO,BO,VO,DTO,POJO来回转换都见鬼去吧。
5、多数据源的无缝切换,0代码修改。首先配置要尽可能的少,第二是底层要适配各种数据库的常用语法。
6、多个数据源的支持更简答话。
7、可动态创建数据源。
8、mvc能不能不要DAO层。
9、方便对接自己写的各种分表分区查询啥的。
10、(50%的初衷)当初年少轻狂,也没女票,对代码爱的痴狂,半夜2点还在撸代码,觉得有必要写一个展示一下自己的能力。
下面就从实际例子来介绍一下这个ORM吧。
整个ORM目前已经适配数据库包括:Oracle、Mysql、SQLserver、Postgresql,可扩展支持及其数据库。
本文打算按以下流程来介绍,着重第三部分:
1.数据源配置;2.对象关系配置;3.增删改查,事务操作、多数据源、动态创建数据源支撑等操作实例。
1、数据源配置
可以同时配置多个数据源,驱动也不用配置(底层直接根据链接查找不同数据库的驱动名),那种万年不变的老驱动,还天天配置个啥,一到配置的时候小伙子们就各种查百度。
#默认数据库配置
db.default.url=jdbc:mysql://localhost:3306/test db.default.username=root db.default.password=root db.default.initPoolSize=5 db.default.maxPoolSize=5 db.default.schema=test #第二个数据源(可无限多个),下面的v2代表第二个数据源的别名,可以自定义,实际操作中用到。 db.v2.url=jdbc:postgresql://10.237.33.127:5432/test db.v2.username=test db.v2.password=123456 db.v2.initPoolSize=5 db.v2.maxPoolSize=5
2、对象关系配置和注入;
我们内部将整个支持MVC三层的对象统一叫做VO,没有绝对的标准。采用注解(annotation)的方式,但已经尽量简化了。后面介绍,mvc各层就一个,可作为前端对象接收表单传值,也可根据annotation配置直接入库。
举个栗子。
先建个表吧!
为了标识这个框架支撑各种字段的能力,所以建了几个不同数据类型的字段。如:字符,CLOB,BLOB,时间戳啥的。
DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` char(32) DEFAULT '' COMMENT '用户id', `name` varchar(255) DEFAULT NULL COMMENT '用户名', `email` varchar(255) DEFAULT NULL COMMENT '邮箱', `head_sculpture` blob COMMENT '头像图片', `resume` longtext COMMENT '简历', `ts` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '时间戳' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
看看vo的注解吧(简书的排版简直让人抓狂,ε=(′ο`*))),不支持插入代码,为了好看这里及截图了,后面get和set我省略了,实际是有的)。
下图中可以看到,只要是和数据库字段名相同的属性都不用注释。不向数据库插入,但是用来接收前端表单数据的属性用@WsdNotDbColumn标注即可。
@WsdTable(name="T_USER") public class UserVo extends ModuleFeatureBean{ /** * 用户id */ @WsdColumn(isId=true) private String id; /** * 用户名 */ private String name; /** * 用户邮箱 */ private String email; /** * 简历 */ @WsdColumn(type=TYPE.CLOB) private String resume; /** * 头像图片 */ @WsdColumn(type=TYPE.BLOB,name="head_sculpture") private byte[] headSculpture; /** * 时间戳 */ private Date ts; /** * 接收界面传递的值用于server的属性,不往数据库插入 */ @WsdNotDbColumn private String token;
这里看一下封装的annotation吧,和ORM相关的也就2个,如下:
1)表的注解(wsdTable):
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WsdTable { /** * 此表在数据的schema名称 * @return */ String schema()default ""; /** * 表名称 * @return */ String name() default ""; /** * 是否sharding 分表 需要在class-path 的名为table_sharding.xml里面查找和当前表名对应的分区配置 * @return */ boolean sharding() default false; }
2)字段注解(WsdColumn关键注解)
这里面定义了不同数据库字段类型的映射关系,以方便一个别名适配各种数据库的类型。
例如NUMBER,可映射到("number","int","tinyint","decimal","float","double","bigint","numeric"),代表不同数据库相同类型的别名。
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WsdColumn { /** * 字段名 * @return */ String name() default ""; /** * 如果使用@see _DEFAULT值,将会使用Field的类型 * @return */ TYPE type() default TYPE._DEFAULT; boolean isId() default false; boolean UUID() default false; enum TYPE{ _DEFAULT, NUMBER(Integer.class,Long.class,Double.class,int.class,double.class,long.class), TIMESTEMP(Date.class), VARCHAR2(String.class), BLOB(byte[].class), CLOB(String.class); private TYPE(){ this.equalClazz = new Class<?>[]{}; } private Class<?>[] equalClazz; private TYPE(Class<?>... equalClazz){ this.equalClazz = equalClazz; } public Class<?>[] getequalClazz(){ return equalClazz; } } /** * 数据库字段分组 * @author 公众号:18岁fantasy * @2014-11-21 @下午7:53:24 */ public static enum DB_TYPE_GROUP{ NUMBER("number","int","tinyint","decimal","float","double","bigint","numeric"), TIMESTAMP("timestamp","timestamp with local time zone","timestamp with time zone"), DATE("date","datetime","time"), STR("varchar2","varchar","char","nvarchar2","nvarchar"), BLOB("blob"), CLOB("clob","ntext","text","longtext"); private String[] dbType; DB_TYPE_GROUP(String... dbType){ this.dbType = dbType; } public String[] getDbType() { return dbType; }
3、配置启动扫描。
这里要说一下,框架设计了一个引擎启动的配置文件,里面会配置要扫描的VO对象的位置,多个要扫描的包用竖线隔开几个。
这样项目启动的时候就会对这些包进行扫描,并将所有扫描到的VO的元数据存入内容中或者缓存服务中。具体过程大家有兴趣可查看源码。
<BootParam> <!-- 要扫描的实体包 --> <modulePackage> <package>com.zlxd.*|core.*</package> </modulePackage> <!-- Spring初始化之前的监听器 --> <beforeSpringListener> </beforeSpringListener> <!-- Spring初始化之后的监听器 --> <afterSpringListener> </afterSpringListener> <!-- 拦截器按顺序执行 --> <interceptors> </interceptors> </BootParam>
扫描的类似产考spring源码自己写的。具体类名为:ModuleParser。大家可以自己看。
4、各种数据操作
从曾删改查、事务操作、多数据源、非spring环境使用进行示例说明。
以下示例都可在类DaoTest里,可查看源码。 ** 已经封装数据库操作常用方法列表**
4.1插入操作 如果对于小型项目,我们一般不写DAO层。直接在service层通过CM,CM为服务获取入口类(当然服务也可以用用spring注入的方式)。
1)对象Insert
这里和常用的框架类似。
/** * 测试插入对象 */ @Test public void testInsert() { try { UserVo u = new UserVo(); u.setId(UUIDGenerator.getUUID()); u.setName("n_n"); u.setEmail("email@163.com"); u.setHeadSculpture(FileUtil.readFileToByteArray(new File("d:\\hp.png"))); u.setResume("简历内容"); u.setTs(new Date()); CM.getDao().insertModule("测试插入对象", u); } catch (Exception e) { e.printStackTrace(); } }
2)批量插入
@Test public void testBatchInsert() throws IOException { try { List<UserVo> users = new ArrayList<UserVo>(); for (int i = 0; i < 50; i++) { UserVo u = new UserVo(); u.setId(UUIDGenerator.getUUID()); u.setName("n_" + i); u.setEmail("email@" + i + ".com"); u.setHeadSculpture(FileUtil.readFileToByteArray(new File("d:\\hp.png"))); u.setResume("简历内容" + i); u.setTs(new Date()); users.add(u); } CM.getDao().batchInsertModule("测试批量插入对象", users); } catch (DaoException e) { e.printStackTrace(); } }
4.2查询操作 1)列表查询
@Test public void testListModule() { try { List<UserVo> users = CM.getDao().listModule("测试基于对象查询列表", UserVo.class, null); CollectionUtil.printCollection(users); } catch (DaoException e) { e.printStackTrace(); } }
2)根据id查询
@Test public void testGetById() { try { String[] idValuds = {"3f1d35b12ab544fdb73badc750e534e2"}; UserVo user = CM.getDao().getModuleById("根据id获取对象", UserVo.class, idValuds); FileUtil.writeByteArrayToFile(new File("d:\\hp2.png"), user.getHeadSculpture()); System.out.println(user); } catch (Exception e) { e.printStackTrace(); } }
3)条件查询
所有的条件组合封装在了WhereCondition这个类中,采用链式封装,操作非常简洁。
@Test public void listModuleByWhereCondition() { try { WhereCondition condition = new WhereCondition(); condition.where().eq("name", "n_21"); List<UserVo> users = CM.getDao().listModule("测试查询", UserVo.class, condition); CollectionUtil.printCollection(users); } catch (DaoException e) { e.printStackTrace(); } }
3)多条件组合查询
WhereCondition封装了对所有sql基本语法的支撑,如:
=、!= 、>、>=、<、<= 、 like、not like 、between and 、in 、 and、 or 、EXISTS 、NOT EXISTS、 IS NULL、 IS NOT NULL;
@Test public void listModuleByMultWhereCondition() { try { WhereCondition condition = new WhereCondition(); condition.where1Eq1().andLike("email", "%email%") .andGreaterThanOrEq("ts", "2019-5-8 11:08:43").and().isNotNull("resume"); List<UserVo> users = CM.getDao().listModule("测试查询", UserVo.class, condition); CollectionUtil.printCollection(users); } catch (DaoException e) { e.printStackTrace(); } }
4)根据sql查询,返回对象列表
@Test public void listModuleBySql() { try { WhereCondition condition = new WhereCondition(); condition.where().greaterThanOrEq("ts", "2019-5-8 14:08:43"); List<UserVo> users = CM.getDao().listModule("测试查询", "select * from t_user ", UserVo.class, condition); CollectionUtil.printCollection(users); } catch (DaoException e) { e.printStackTrace(); } }
5)分页查询
分页主要看Pager这个类,大家可以查看源码。
@Test public void listPaginationModule() { try { WhereCondition condition = new WhereCondition(); condition.where().greaterThanOrEq("ts", "2019-5-8 14:08:43"); Pager pager = new Pager(0, 10); PagerData<UserVo> users = CM.getDao().getPagerModuleList("测试分页查询", UserVo.class, condition, pager); System.out.println("总条数:" + users.getTotal()); CollectionUtil.printCollection(users.getRows()); } catch (DaoException e) { e.printStackTrace(); } }
分页主要看Pager这个对象。 4.3修改操作 根据id修改
@Test public void testUpdateById() { try { UserVo user = new UserVo(); user.setId("3cccc60b170848b1a53e3db9d519bd48"); user.setName("new_name"); CM.getDao().updateModuleById("测试根据id更新", user, new String[] {"headSculpture"});//可设置哪些字段不更新 } catch (DaoException e) { e.printStackTrace(); } }
自动排除值为空的属性。 ` @Test public void updateModuleByIdExecuteNull() { try { UserVo user = new UserVo(); user.setId("bc17b776523241479354f6a4e6a2e26e"); user.setName("new_name");
CM.getDao().updateModuleByIdExecuteNull("测试根据id更新不包含值为空的对象", user); } catch (DaoException e) { e.printStackTrace(); }
}`
手动设置哪些字段不更新 @Test public void saveOrUpdateModuleById() { try { UserVo user = new UserVo(); user.setId("bc17b776523241479354f6a4e6a2e261"); user.setName("new_name");
CM.getDao().saveOrUpdateModuleById("save or update", user, new String[] {"headSculpture"}); } catch (DaoException e) { e.printStackTrace(); }
} 2)根据条件批量修改
@Test public void testBatchUpdate() { try { UserVo user = new UserVo(); user.setName("new_name"); WhereCondition condition = new WhereCondition(); condition.where().lessThan("Ts", "2019-05-08 15:47:11"); CM.getDao().updateModule("测试删除", user, condition, new String[] {"headSculpture"});//可设置哪些字段不更新 } catch (DaoException e) { e.printStackTrace(); } }
4.5 SaveOrUpdate 根据数据库是否有着id来判断新增还是插入。
@Test public void saveOrUpdateModuleById() { try { UserVo user = new UserVo(); user.setId("bc17b776523241479354f6a4e6a2e261"); user.setName("new_name"); CM.getDao().saveOrUpdateModuleById("save or update", user, new String[] {"headSculpture"}); } catch (DaoException e) { e.printStackTrace(); } }
4.4删除操作 条件删除
@Test public void testDelete() { WhereCondition condition = new WhereCondition(); condition.where().isNotNull("ts").andBetween("ts", new BetweenValue("2019-5-8 11:17:51", "2019-5-8 16:17:51", true, true)); try { CM.getDao().deleteModule("测试删除", UserVo.class, condition); } catch (DaoException e) { e.printStackTrace(); } }
2)根据id删除
@Test public void testDeleteById() { try { CM.getDao().deleteModuleById("测试删除", UserVo.class, new Object[] {"06e773fb69404bf7b64a156bada11846"}); } catch (DaoException e) { e.printStackTrace(); } }
4.5事务操作 在一个事务中执行多个ddl操作。
@Test public void doInSingleTransationCircle() { try { UserVo user = new UserVo(); user.setId("bc17b776423241479354f6a4e6a2e261"); user.setName("new_name"); Dao dao = CM.getDao(); CM.getDao().doInSingleTransationCircle("事务操作", new SingleTransationCircleWithOutResult() { @Override public void actionInCircle() throws RuntimeException { try { dao.insertModule("插入", user); user.setEmail("email@1.1"); WhereCondition condition = new WhereCondition(); condition.where().lessThan("Ts", "2019-05-08 15:47:11"); dao.updateModule("修改", user, condition); dao.deleteModuleById("删除", user); } catch (DaoException e) { throw new RuntimeException(e); } } }); } catch (DaoException e) { e.printStackTrace(); } }
4.6多数据源操作 1.首先需提前在配置文件中配置多数据源信息。配置很简单,个支持多个。如下:
#默认数据库配置 db.default.url=jdbc:mysql://localhost:3306/test db.default.username=root db.default.password=root db.default.initPoolSize=5 db.default.maxPoolSize=5 db.default.schema=test #其他数据源 db.pg.url=jdbc:postgresql://10.237.33.127:5432/test db.pg.username=test db.pg.password=123456 db.pg.initPoolSize=5 db.pg.maxPoolSize=5
2.在CM中定义。
public class CM extends CmCommon{ private static final String PG_DBNAME = "pg"; /** * 获另外的的数据源操作类 */ public static Dao getPGDao() { return getDao(PG_DBNAME); } }
3.然后就可以直接调用了
@Test public void multDataSource() { try { //默认数据源 CM.getDao().listModule("测试基于对象查询列表", UserVo.class, null); //另外的数据源 CM.getPGDao().listModule("测试基于对象查询列表", UserVo.class, null); } catch (DaoException e) { e.printStackTrace(); } }
4.7动态创建数据源 主要用在通过界面添加数据源的情况,比如动态添加一个数据库对其进行定时监控,或者ETL中的数据源动态配置
/** * 可在非spring环境下使用各种方法。常用场景如,比如要动态添加一个对数据库对其进行监控 */ public static void main(String[] args) throws Exception { //数据源参数 DataSourceProperty dataSourceProperty = new DataSourceProperty(); dataSourceProperty.setJdbcUrl("jdbc:mysql://localhost:3306/test"); dataSourceProperty.setUsername("root"); dataSourceProperty.setPassword("root"); dataSourceProperty.setInitialSize(1); dataSourceProperty.setMaxActive(1); Dao dmDao = DaoFactory.createDao("动态数据源数据源", dataSourceProperty); List<Map<String, Object>> users = dmDao.listMap("查询", "select * from t_user where ts<?", new Object[] {"2019-5-8 15:08:43"}); CollectionUtil.printCollection(users); }
相关推荐
- Spring WebFlux vs. Spring MVC(springboot是什么)
-
背景随着异步I/O和Netty等框架的流行,响应式编程逐渐走入大众的视野。但是,响应式编程本身并不是太新的概念,这个术语最早出现在1985年DavidHarel和AmirPnue...
- 深度解析微服务高并发:适配SpringMVC框架适配模块及实现原理
-
适配主流框架如果不借助Sentinel提供的适配主流框架的模块,则在使用Sentinel时需要借助try-catchfinally将要保护的资源(方法或代码块)包起来,在目标方法或代码块执行之前,调...
- Spring MVC 底层原理深度解析:从请求到响应的全链路拆解
-
一、Servlet容器与DispatcherServlet的启动博弈1.Tomcat初始化阶段java//Tomcat初始化流程StandardContext#startInterna...
- 改造总结之传统SpringMVC架构转换为SpringBoot再到集群
-
改造出发点,是基于现在服务都在向上云的目标前进,传统SpringMVC难以满足项目持续构建、服务节点任意扩展的需求,所以开始了历史项目的改造。项目改造考虑的主要是兼容以前的业务代码,以及session...
- SpringBoot3 整合 Spring MVC 全解析:开启高效 Web 开发之旅
-
在当今的JavaWeb开发领域,Spring框架家族无疑占据着重要的地位。其中,SpringBoot3和SpringMVC更是开发者们构建强大、高效Web应用的得力工具。今天,...
- 一文读懂SpringMVC(一文读懂!残疾人低保边缘家庭能领的超实用福利政策)
-
1.SpringMVC定义1.1.MVC定义Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据View(视图):是应用程序中处理数据显示的部分。通常...
- 69 个Spring mvc 全部注解:真实业务使用案例说明(必须收藏)
-
SpringMVC框架的注解为Web开发提供了一种简洁而强大的声明式方法。从控制器的定义、请求映射、参数绑定到异常处理和响应构建,这些注解涵盖了Web应用程序开发的各个方面。它们不仅简化了编码工作,...
- Spring MVC工作原理:像拼积木一样构建Web应用
-
SpringMVC工作原理:像拼积木一样构建Web应用在Java的Web开发领域,SpringMVC无疑是一个让人又爱又恨的存在。它像一位神通广大的积木搭建大师,将一个个分散的功能模块巧妙地拼接在...
- 5千字的SpringMVC总结,我觉得你会需要
-
思维导图文章已收录到我的Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary概述SpringMVC再熟悉不过的框架了,因为现在最火的...
- SpringMVC工作原理与优化指南(springmvc工作原理和工作流程)
-
SpringMVC工作原理与优化指南在现代Java开发中,SpringMVC无疑是构建Web应用程序的首选框架之一。它以其优雅的设计和强大的功能吸引了无数开发者。那么,SpringMVC究竟是如何工作...
- Spring MVC框架源码深度剖析:从入门到精通
-
SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...
- 3000字搞明白SpringMVC工作流程、DispatcherServlet类、拦截器!
-
SpringMVC基础虽然SpringBoot近几年发展迅猛,但是SpringMVC在Web开发领域仍然占有重要的地位。本章主要讲解SpringMVC的核心:DispatcherServlet类...
- 多年经验大佬用2000字透彻解析SpringMVC的常用注解及相关示例
-
SpringMVC注解SpringMVC框架提供了大量的注解,如请求注解、参数注解、响应注解及跨域注解等。这些注解提供了解决HTTP请求的方案。本节主要讲解SpringMVC的常用注解及相关示例...
- 知乎热议:如何成为前端架构师,赚百万年薪?
-
作者|慕课网精英讲师双越最近有一条知乎热议:从一个前端工程师,如何根据目标,制定计划,才能快速进阶成为前端架构师?不久之前我参与了一次直播,讲到了自己对于Web前端架构师的理解。架构师这个角色...
- 学习笔记-前端开发架构设计(前端架构设计方案)
-
前端开发的技术选项主要包含以下几点,下面对一些名词概念的解释做了笔记:1、分层架构:把功能相似,抽象级别相近的实现进行分层隔离优势:松散耦合(易维护,易复用,易扩展)常见分层方式:MVC,MVVM2、...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Spring WebFlux vs. Spring MVC(springboot是什么)
- 深度解析微服务高并发:适配SpringMVC框架适配模块及实现原理
- Spring MVC 底层原理深度解析:从请求到响应的全链路拆解
- 改造总结之传统SpringMVC架构转换为SpringBoot再到集群
- SpringBoot3 整合 Spring MVC 全解析:开启高效 Web 开发之旅
- 一文读懂SpringMVC(一文读懂!残疾人低保边缘家庭能领的超实用福利政策)
- 69 个Spring mvc 全部注解:真实业务使用案例说明(必须收藏)
- Spring MVC工作原理:像拼积木一样构建Web应用
- 5千字的SpringMVC总结,我觉得你会需要
- SpringMVC工作原理与优化指南(springmvc工作原理和工作流程)
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- bootstrap框架 (43)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- express框架 (43)
- springmvc框架 (49)
- scrapy框架 (52)
- beego框架 (42)
- java框架spring (43)
- grpc框架 (55)
- orm框架有哪些 (43)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)