赞
踩
我为什么要写这么一篇文章呢?因为我从事java开发20余载,看着各种jdbc框架、应用框架一直在演变升级,却始终达不到我对Java项目的快速开发要求,所以忍不住把我多年经验积累的快速开发框架共享出来,希望能给java界带来一丝凉风,让大家体验不一样的开发。不过,认为springboot和mybatis是最好的同学可以不用看了,因为我不一个主流技术控,我是一个完美主义者,我追求的是开发的效率和质量,并且把代码的简洁放在了首位,享受着敲代码带给我的快乐。
这次介绍的是我个人比较喜欢的优点,如果不是为了这些优点,那我就没必要造轮子了,但是我的文笔不行,所以这篇文章只是一个开端,我先介绍我个人最喜欢的功能,也就是这个快速开发框架的亮点,我以后会逐步把这个快速开发框架做为一个教程全部写出来。
支持子查询和联表查询,每个Select对象都可以被重用。
- // 子查询示例:(DF是DaoFactory的缩写。Select、RowSet对象不需要我解释了吧?)
- String[] orderNumbers=["1111","2222"];
- ClientOrderSelect cos=DF.clientOrderSelect().setClientOrder(orderNumbers);
- OrderGoodsRowSet ogrs=DF.orderGoodsSelect().setOrderId(cos).toRowSet();
- // 对应sql:select * from order_goods where order_id in (select order_id from client_order where orderNumber in ('1111','2222'))
Select对象可直接获取SQL,可重用,可以子表、联表查询,直接生成sql,加个select.useCache(true)就可以启用缓存,可以选择是否打印sql日志,可以极方便的使用or条件等等,这些优点都是Mybatis所没有的。
- //最简单的sql查询,可在执行sql之前使用.toString()打印出可直接执行的sql,方便直接复制客户端直接运行
- TestUserSelect tus=new TestUserSelect(conn).setUserCode("admin").setValid(true);
- esdk.tool.assertEquals(tus.toString(),"SELECT * FROM test_user WHERE user_code='admin' AND valid=1");
-
- //非常方便地使用Redis二级缓存。两种方式:true:默认15分钟;1000:1000秒。
- tus.useCache(true).toRowSet()
- tus.useCache(1000).toRowSet()
-
- //默认打印SQL日志,但是遇到一些频率非常高但意义不大的sql,看着就烦,有什么办法过滤掉呢?
- tus.showSql(false).toRowSet()
-
- //写or条件好麻烦,懂的人都懂,有什么更好的表达方式呢?我认为最完美的表达方式是这样的:
- tus.setValid(true).setSex("男");
- //tus.md对应的是TestUserMetaData.java静态类,是一个表基础信息类,可读取该表的所有信息,如字段名,备注名,主键名,唯一索引,默认值等。但为了方便表达,我后面都会有字符串表示,但实际项目上我不会直接用字符串,而是用MetaData类。
- tus.or(tus.md.UserName,"张三","login_name","like","test%")
- //输出SQL: SELECT * FROM test_user WHERE valid=1 and sex="男" and (user_name='张三' OR login_name like 'test%')
-
- //联表查询,这是我认为最好的表达方式了,也是esdk这个ORM框架的一个核心优点,而mybatisplus一直都做不到:
- TestCenterSelect tcs=DF.testCenterSelect().setCenterId(108).setOrder("category")
- JoinSelect cs=new JoinSelect(conn,tus,tcs).on(tcs,tds);
- esdk.tool.assertEquals(cs.getSQL(),"SELECT tu.*,tc.* from test_user tu left join test_center tc on tc.center_id=tu.center_id where tu.valid=1 and tu.sex='男' and tc.center_id=108 ORDER BY tc.category LIMIT 50");

说一下批处理插入/批更新量量,10万条记录可以批量分段插入,做数据迁移太方便了:
- JDBCTemplate jd=new JDBCTemplate(connSrc,"select * from "+srcTableName+" where id>? order by id limit ? offset ?",lastId,limit,offset);
- BatchInsert bi=new BatchInsert(distTableName ,connDist).
- ABResultSet<IRow> rs=jd.toABResultSet();
- BatchInsert bi=new BatchInsert(distTableName,connDist);
- log.info("正在读取源数据");
- TimeMeter tm1=new TimeMeter();
- for(IRow row=rs.getNextRow();row!=null;row=rs.getNextRow()){
- bi.addValue(row);
- }
- log.info("正在按10000条分段写入目标数据库");
- int count=bi.perform(10000,true);
- // 超过1万的日志打印出来的话会刷爆屏的,更会占用大量io和内存,优化为超过10条就省略。
- 2023-03-09 17:42:31,337 esdk warn 耗时:756ms, SQL:Insert Into member_point_record (id,points_account_id,points_account_code,trade_id,points,status,expired_date,expired,remark,create_time,member_model_id,channel,channel_no,effective_date,tenant_id,create_person,update_person,update_time,dr,migrated_time,instance_id,extension,current_points,available_points,trade_type,member_id,order_no,point_type,sync_type,businessId,shop_code,record_id,un_freeze_time,business_id,member_no,before_point,after_point) values (3345116,1328265456742041456,'',null,20,1,'2021-02-25 20:58:30',0,'获得积分','2020-02-26 20:58:30',null,'官方商城',null,null,null,null,null,'2023-01-10 16:24:11',0,null,null,'946.00',null,null,2,1328265456506111730,null,null,0,null,null,null,'2020-02-26 20:58:30','3345116',1312327918374372345,'1001519678',946,966)...共插入10000条记录;
为什么要用nutzboot?因为快,好用,上手容易。目前应用启动只需要2秒(Jetty启动占了1.5秒),占用内存不到50M,真正做到了“微”服务。
Connected to the target VM, address: '127.0.0.1:49653', transport: 'socket' 2023-03-26 10:17:03,173 [ INFO][SimpleBannerPrinter:34] _ _ ______ ___ | \ | || ___ \ ______ ______ ______ ______ ______| \ \ | \| || |_/ / |______|______|______|______|______| |\ \ | . ` || ___ \ ______ ______ ______ ______ ______| | > > | |\ || |_/ / |______|______|______|______|______| |/ / \_| \_/\____/ |_/_/ :: Nutz Boot :: (2.4.2.v20201205) 2023-03-26 10:17:03,322 [ INFO][AnnotationIocLoader:47] > scan 'com.baijian.cas' 2023-03-26 10:17:03,421 [ INFO][AnnotationIocLoader:99] > add 'permissionFilter ' - com.baijian.cas.filter.PermissionFilter 2023-03-26 10:17:03,432 [ INFO][AnnotationIocLoader:99] > add 'mainLauncher ' - com.baijian.cas.MainLauncher 2023-03-26 10:17:03,440 [ INFO][AnnotationIocLoader:99] > add 'loginModule ' - com.baijian.cas.module.LoginModule 2023-03-26 10:17:03,461 [ INFO][AnnotationIocLoader:99] > add 'sysButtonModule ' - com.baijian.cas.module.SysButtonModule 2023-03-26 10:17:03,469 [ INFO][AnnotationIocLoader:99] > add 'sysDictModule ' - com.baijian.cas.module.SysDictModule 2023-03-26 10:17:03,474 [ INFO][AnnotationIocLoader:99] > add 'sysMenuModule ' - com.baijian.cas.module.SysMenuModule 2023-03-26 10:17:03,478 [ INFO][AnnotationIocLoader:99] > add 'sysOrgModule ' - com.baijian.cas.module.SysOrgModule 2023-03-26 10:17:03,479 [ INFO][AnnotationIocLoader:99] > add 'sysRoleModule ' - com.baijian.cas.module.SysRoleModule 2023-03-26 10:17:03,480 [ INFO][AnnotationIocLoader:99] > add 'sysTenantModule ' - com.baijian.cas.module.SysTenantModule 2023-03-26 10:17:03,482 [ INFO][AnnotationIocLoader:99] > add 'sysUserModule ' - com.baijian.cas.module.SysUserModule 2023-03-26 10:17:03,483 [ INFO][AnnotationIocLoader:99] > add 'testModule ' - com.baijian.cas.module.TestModule 2023-03-26 10:17:03,487 [ INFO][AnnotationIocLoader:99] > add 'uploadModule ' - com.baijian.cas.module.UploadModule 2023-03-26 10:17:03,488 [ INFO][AnnotationIocLoader:99] > add 'schedule ' - com.baijian.cas.schedule.Schedule 2023-03-26 10:17:03,488 [ INFO][AnnotationIocLoader:99] > add 'sysRolePermissionService ' - com.baijian.cas.service.SysRolePermissionService 2023-03-26 10:17:03,489 [ INFO][AnnotationIocLoader:47] > scan 'com.baijian.framework.filter' 2023-03-26 10:17:03,498 [ INFO][AnnotationIocLoader:99] > add 'accessControlAllowOriginFilter ' - com.baijian.framework.filter.AccessControlAllowOriginFilter 2023-03-26 10:17:03,500 [ INFO][AnnotationIocLoader:99] > add 'authorizationFilter ' - com.baijian.framework.filter.AuthorizationFilter 2023-03-26 10:17:03,501 [ INFO][AnnotationIocLoader:99] > add 'browserCacheFilter ' - com.baijian.framework.filter.BrowserCacheFilter 2023-03-26 10:17:03,502 [ INFO][AnnotationIocLoader:99] > add 'corsWebFilterFace ' - com.baijian.framework.filter.CorsWebFilterFace 2023-03-26 10:17:03,503 [ INFO][AnnotationIocLoader:47] > scan 'com.baijian.framework.processor' 2023-03-26 10:17:03,512 [ INFO][AnnotationIocLoader:99] > add 'accessLogProcessor ' - com.baijian.framework.processor.AccessLogProcessor 2023-03-26 10:17:03,514 [ INFO][AnnotationIocLoader:99] > add 'exceptionProcessor ' - com.baijian.framework.processor.ExceptionProcessor 2023-03-26 10:17:03,514 [ INFO][AnnotationIocLoader:99] > add 'moduleInterceptor ' - com.baijian.framework.processor.ModuleInterceptor 2023-03-26 10:17:03,515 [ INFO][AnnotationIocLoader:47] > scan 'com.baijian.framework.service' 2023-03-26 10:17:03,521 [ INFO][AnnotationIocLoader:99] > add 'registerService ' - com.baijian.framework.service.RegisterService 2023-03-26 10:17:03,522 [ INFO][AnnotationIocLoader:99] > add 'restApiService ' - com.baijian.framework.service.RestApiService 2023-03-26 10:17:03,523 [ INFO][AnnotationIocLoader:47] > scan 'org.nutz.boot.starter' 2023-03-26 10:17:03,543 [ INFO][AnnotationIocLoader:99] > add 'whaleFilterStarter ' - org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter 2023-03-26 10:17:03,545 [ INFO][AnnotationIocLoader:99] > add 'nutFilterStarter ' - org.nutz.boot.starter.nutz.mvc.NutFilterStarter 2023-03-26 10:17:03,546 [ INFO][AnnotationIocLoader:99] > add 'jettyStarter ' - org.nutz.boot.starter.jetty.JettyStarter 2023-03-26 10:17:03,567 [ INFO][AnnotationIocLoader:99] > add 'preventDuplicateSubmitStarter ' - org.nutz.boot.starter.PreventDuplicateSubmitStarter 2023-03-26 10:17:03,567 [ INFO][AnnotationIocLoader:99] > add 'dataSourceStarter ' - org.nutz.boot.starter.jdbc.DataSourceStarter 2023-03-26 10:17:03,568 [ INFO][AnnotationIocLoader:99] > add 'druidWebStatFilterStarter ' - org.nutz.boot.starter.jdbc.DruidWebStatFilterStarter 2023-03-26 10:17:03,568 [ INFO][AnnotationIocLoader:99] > add 'druidWebStatServletStarter ' - org.nutz.boot.starter.jdbc.DruidWebStatServletStarter 2023-03-26 10:17:03,569 [ INFO][AnnotationIocLoader:99] > add 'nbServletContextListener ' - org.nutz.boot.starter.servlet3.NbServletContextListener 2023-03-26 10:17:03,581 [ INFO][NutIoc:130] ... NutIoc init complete 2023-03-26 10:17:03,710 [ INFO][log:170] Logging initialized @2335ms to org.eclipse.jetty.util.log.Slf4jLog 2023-03-26 10:17:03,968 [ INFO][Server:375] jetty-9.4.41.v20210516; built: 2021-05-16T23:56:28.993Z; git: 98607f93c7833e7dc59489b13f3cb0a114fb9f4c; jvm 17.0.4.1+1-LTS 2023-03-26 10:17:04,124 [ INFO][AnnotationConfiguration:473] Scanning elapsed time=0ms 2023-03-26 10:17:04,132 [ INFO][StandardDescriptorProcessor:277] NO JSP Support for /cas, did not find org.eclipse.jetty.jsp.JettyJspServlet 2023-03-26 10:17:04,163 [ INFO][session:334] DefaultSessionIdManager workerName=node0 2023-03-26 10:17:04,164 [ INFO][session:339] No SessionScavenger set, using defaults 2023-03-26 10:17:04,167 [ INFO][session:132] node0 Scavenging every 600000ms 2023-03-26 10:17:04,267 [ INFO][DruidWebStatServletStarter:74] druid stat view random user=druid password=39qaaf3d5kh5prg18p0drevu77 2023-03-26 10:17:04,297 [ INFO][NutFilter:85] NutFilter[nutz] starting ... 2023-03-26 10:17:04,301 [ INFO][ErrorResourceLocation:28] [loc=D:\work\Projects\java\gitee\nzb-cas\esdk-nzb-cas\static\WEB-INF\classes]not exist 2023-03-26 10:17:04,306 [ INFO][NutLoading:39] Nutz Version : 1.r.69.v20220215 2023-03-26 10:17:04,306 [ INFO][NutLoading:40] Nutz.Mvc[nutz] is initializing ... 2023-03-26 10:17:04,311 [ INFO][NutLoading:146] Build URL mapping by org.nutz.mvc.impl.UrlMappingImpl ... 2023-03-26 10:17:04,422 [ INFO][BrowserCacheFilter:24] com.baijian.framework.filter.BrowserCacheFilter@203c20cf has inited 2023-03-26 10:17:04,604 [ INFO][NutFilePool:28] Init file-pool by: D:/work/Projects/java/gitee/nzb-cas/esdk-nzb-cas/static/WEB-INF/tmp [2000] 2023-03-26 10:17:04,604 [ INFO][NutFilePool:50] file-pool.cursor: 0 2023-03-26 10:17:04,972 [ INFO][NutLoading:208] Found 97 module methods 2023-03-26 10:17:04,981 [ INFO][NutLoading:128] Nutz.Mvc[nutz] is up in 675ms 2023-03-26 10:17:04,982 [ INFO][NutFilter:117] exclusionsPrefix = ^(/druid/) 2023-03-26 10:17:05,002 [ INFO][ContextHandler:916] Started o.e.j.w.WebAppContext@8b91134{/cas,[file:///D:/work/Projects/java/gitee/nzb-cas/esdk-nzb-cas/static/],AVAILABLE} 2023-03-26 10:17:05,059 [ INFO][AbstractConnector:331] Started ServerConnector@2102a4d5{HTTP/1.1, (http/1.1)}{0.0.0.0:8081} 2023-03-26 10:17:05,059 [ INFO][Server:415] Started @3686ms 2023-03-26 10:17:05,068 [ INFO][Schedule:32] Schedules is starting... 2023-03-26 10:17:05,080 [ INFO][MainLauncher:64] mainLauncher init jdbc:mysql://db.server:3306/cas?useUnicode=true&characterEncoding=utf8&autoReconnect=true, driver:com.mysql.cj.jdbc.Driver, MinIdle:0, MaxActive:10, ActiveCount:0 jedis host: redis.server , max connection: 100 2023-03-26 10:17:05,167 [ INFO][NbApp:219] NB started : 2039ms
完善的请求日志及SQL日志,通过请求ID追踪全过程,并写到第三方日志系统(如PlumeLog),达到快速定位、解决问题的效果。
- 2023-03-26 10:20:27,340 nzb-cas info 请求ID:1681795822570, 耗时:2ms, SQL:SELECT * FROM sys_file_upload WHERE file_id=425283962380894208;
- 2023-03-26 10:20:27,486 [ INFO][AccessLogHandler:23] 请求ID:1681795822570, 耗时:158ms, 用户:张三, 请求:/cas/pic/425283962380894208?accessToken=5d70bab7-ba9f-40f8-b5e1-84733d37a52f, 返回:java.io.FileInputStream@23ea81a7
-
- 2023-03-26 10:22:07,387 nzb-cas info 请求ID:1681893112718, 耗时:5ms, SQL:SELECT user_id,user_type,login_name,user_name,user_nick,sex,mobile,email,remark,avatar_url,tenant_id,login_tenant_id,tenant_ids,tenant_names,org_ids,org_names,role_ids,role_names,rank,is_enabled,valid,create_time,create_user_id,create_user_name,update_time,update_user_id,update_user_name,version,tenant_name,login_tenant_code,login_tenant_name FROM sys_user_view WHERE valid=1 AND user_nick LIKE '小%' AND tenant_id=108 AND user_type='sys' ORDER BY user_id desc LIMIT 10 OFFSET 0;
- 2023-03-26 10:22:07,390 [ INFO][AccessLogHandler:23] 请求ID:1681893112718, 耗时:14ms, 用户:张三, 请求:/cas/system/user/list.do?roleIds=&orgIds=&userNick=小*&pageSize=10&userType=sys&page=1&tenantIds=, 返回:{"totalCount":1,"page":1,"pageCount":1,"pageSize":10,"start":0,"rows":[{"userId":"42634...
通过数据库反向生成易于业务扩展的前后端代码,实现真正的低代码。
- public static void main(String[] args) throws Exception{
- Stopwatch sw=new Stopwatch();
- //通过数据库生成数据表ORM对象
- genOrmFiles(esdk.str.splits("sys_role,sys_user,sys_org"));
- //通过ORM对象生成前后端代码,包括增删改查、导入、导出。
- genModules(true,true,true,"sys_user","sys_org");
- sw.stop();
- }
- /**
- * @业务描述 组织机构
- * @author 请填写姓名或邮箱
- * @since 2023-02-20 10:18:13
- */
- @IocBean(singleton=false) //注意:Module不能是单例例式,有Field
- @At("/system/org")
- @Ok("raw")
- @Fail("http:500")
- public class SysOrgModule extends ProjectModule<SysOrgRow,SysOrgViewRow>{
- private static Log log=esdk.log();
- @Override protected Class getModelClass(){return SysOrgRow.class;}
-
- /**通用查询接口,已实现指定字段的模糊查询、时间或数值类型的范围查询,并优先使用数据库视图输出字段,已实现分页或导出excel功能*/
- @At @GET
- public Object list(SysOrgViewRow model,Search search) throws Exception{
- setListLikeFields();
- Object pageOrRs=super.list(model,search);
- return pageOrRs;
- }
-
- /**调用list()时,继承toPage方法可以对Select/ABRowSet对象进行业务处理,不需要改动的话可以删除*/
- @Override
- protected Page toPage(IPageSelect pageSelect,Search search) throws Exception{
- SysOrgViewSelect select=(SysOrgViewSelect)pageSelect;
- return toPage(select.toRowSet(),search);
- }
-
- /**通用单条记录获取接口,优先使用数据库视图的字段输出*/
- @At @GET
- public SysOrgViewRow query(Long orgId) throws SQLException{
- return super.query(orgId);
- }
-
- /**新增单条记录,父类已实用通用新增方法*/
- @At @POST
- public Response insert(SysOrgRow model) throws Exception{
- return super.insert(model);
- }
-
- /**修改单条记录,父类已实用通用修改方法*/
- @At @POST
- public Response update(SysOrgRow model) throws Exception {
- return super.update(model);
- }
-
- /**调用insert、update、save方法时提交前的处理*/
- @Override
- protected Response beforeFlush(SysOrgRow db) throws Exception{
- return super.beforeFlush(db);
- }
-
- /**调用insert、update、save方法时提交后的处理*/
- @Override
- protected Response afterFlush(SysOrgRow db) throws Exception{
- return super.afterFlush(db);
- }
-
- /**通用保存接口,通过主键判断新增或修改*/
- @At @POST
- public Response save(SysOrgRow model) throws Exception {
- return super.save(model);
- }
-
- /**删除多条记录,父类已实用通用删除方法*/
- @At @POST
- public Response delete(Long[] ids) throws Exception{
- // DF.orgDao().delete(DF.orgSelect().setPrimaryKey(ids).toRowSet());
- return super.delete(ids);
- }
-
- /**查询下拉框,父类已实用通用查询方法*/
- @At @GET
- public ABRowSet dropdown(SysOrgRow model,Search search) throws SQLException{
- return super.dropdown(model,search);
- }
-
- /**导出excel文件*/
- @At @GET @Ok("void")
- public void export(SysOrgViewRow model,Search search) throws Exception{
- super.export(model,search);
- }
-
- /**excel文件批量导入*/
- @At("/import") @POST
- @AdaptBy(type=UploadAdaptor.class, args={"${app.root}/WEB-INF/tmp"})
- public Response uploadExcel(@Param("file") TempFile file,ServletContext sc) throws Exception{
- ri=new SysOrgUploadService().importExcel(file,true,true);
- return ri;
- }
- }

项目打包部署
- #部署增量包
- gradle deployJar
-
- #部署全量包
- gradle deployJarWithLib
-
- #前端编译打包,放到jetty的静态目录static下
- gradle npmRunBuild
esdk-nzb-cas共享项目地址:https://gitee.com/ffychina/nzb-cas
esdk快速开发工具项目地址:https://gitee.com/ffychina/esdk
今天就说这么多了,后面逐步完善,欢迎指点交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。