厌烦了MyBatis
和JPA
(Hibernate
)的冗余配置和繁琐使用,以及XML拷来拷去,写那么多Mapper要是直接用JDBC早就写完了,如果使用一个框架/工具比不使用而带来更多的心智负担,那还不如不用。其实,直接使用JDBC还是蛮干净和高性能的,只要解决其三个痛点:
能否造一个更好的轮子?
于是,我花了十多个晚上,结合之前在C技术栈中的设计和经验,结合JAVA特点,写了sqlaction。
sqlaction
是自动生成JDBC代码的数据库持久层工具,它为应用提供了类似MyBatis
和Hibernate
操作数据库能力,但更轻量级和几乎消除了所有的手工冗余编码,提高开发效率,也易于与其它框架搭配使用。sqlaction
读取数据库中的表结构元信息和少量配置文件信息(SQL),自动生成数据库表实体类,自动生成基于JDBC的SQL动作方法代码,自动生成拦截器框架等代码,应用调用其自动生成的代码就能极其快捷的操作数据库,同时还拥有JDBC的高性能,更重要的是开发者能直接看到底层操作代码,增加自主可控,没有低效的反射,没有复杂的热修改字节码,没有庞大笨重的隐藏核心。
放一个DEMO感受一下:
ddl.sql
CREATE TABLE `sqlaction_demo` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `name` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '名字', `address` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`), KEY `sqlaction_demo` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
只依赖数据库连接库和作者另一个JSON解析器okjson
,引入mysql-connector-java-X.Y.Z.jar
、okjson-0.0.9.0.jar
。
建立包目录,在包目录或上级某一级目录中创建数据库连接配置文件dbserver.conf.json
,工具会从执行目录开始往上查找,只要某一级目录中存在这个配置文件即可。
{ "driver" : "com.mysql.jdbc.Driver" , "url" : "jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT" , "user" : "calvin" , "pwd" : "calvin" }
在包目录或上级某一级目录中创建SQL动作配置文件sqlaction.conf.json
,工具会从执行目录开始往上查找,只要某一级目录中存在这个配置文件即可。
{ "database" : "calvindb" , "tables" : [ { "table" : "sqlaction_demo" , "sqlactions" : [ "SELECT * FROM sqlaction_demo" , "SELECT * FROM sqlaction_demo WHERE name=?" , "INSERT INTO sqlaction_demo" , "UPDATE sqlaction_demo SET address=? WHERE name=? @@METHOD(updateAddressByName)" , "DELETE FROM sqlaction_demo WHERE name=?" ] } ] , "javaPackage" : "xyz.calvinwilliams.sqlaction.demo" }
sqlaction
工具我把执行命令行包成批处理文件后执行,欢迎懂Eclipse
插件开发的同学帮我写个插件 :)
pp.bat
java -Dfile.encoding=UTF-8 -classpath "D:\Work\mysql-connector-java-8.0.15\mysql-connector-java-8.0.15.jar;%USERPROFILE%\.m2\repository\xyz\calvinwilliams\okjson\0.0.9.0\okjson-0.0.9.0.jar;%USERPROFILE%\.m2\repository\xyz\calvinwilliams\sqlaction\0.2.1.0\sqlaction-0.2.1.0.jar" xyz.calvinwilliams.sqlaction.SqlActionGencode pause
注意:使用Maven
管理的项目,在添加sqlaction
依赖声明后,maven
会自动下载sqlaction
以及其依赖okjson
的jar到C:\User\用户名\.m2\repository\xyz\calvinwilliams\
下。sqlaction
依赖声明见最后面“关于本项目”章节。
执行pp.bat,工具sqlaction
会从执行目录开始往上查找读入dbserver.conf.json
和sqlaction.conf.json
并自动生成所有代码。
////////////////////////////////////////////////////////////////////////////// /// sqlaction v0.0.8.0 /// Copyright by calvin<calvinwilliams@163.com,calvinwilliams@gmail.com> ////////////////////////////////////////////////////////////////////////////// --- dbserverConf --- dbms[mysql] driver[com.mysql.jdbc.Driver] url[jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT] user[calvin] pwd[calvin] --- sqlactionConf --- database[calvindb] table[sqlaction_demo] sqlaction[SELECT * FROM sqlaction_demo] sqlaction[SELECT * FROM sqlaction_demo WHERE name=?] sqlaction[INSERT INTO sqlaction_demo] sqlaction[UPDATE sqlaction_demo SET address=? WHERE name=? @@METHOD(updateAddressByName)] sqlaction[DELETE FROM sqlaction_demo WHERE name=?] SqlActionTable.getTableInDatabase[sqlaction_demo] ... ... ... ... *** NOTICE : Write SqlactionDemoSAO.java completed!!!
如果没有出现*** ERROR : ...
说明工具执行成功,在执行所在目录中自动生成了一个JAVA源代码文件
SqlactionDemoSAO.java
// This file generated by sqlaction v0.2.1.0 package xyz.calvinwilliams.sqlaction.demo; import java.math.*; import java.util.*; import java.sql.Time; import java.sql.Timestamp; import java.sql.Connection; import java.sql.Statement; import java.sql.PreparedStatement; import java.sql.ResultSet; public class SqlactionDemoSAO { int id ; // 编号 String name ; // 名字 String address ; // 地址 int _count_ ; // defining for 'SELECT COUNT(*)' // SELECT * FROM sqlaction_demo public static int SELECT_ALL_FROM_sqlaction_demo( Connection conn, List<SqlactionDemoSAO> sqlactionDemoListForSelectOutput ) throws Exception { Statement stmt = conn.createStatement() ; ResultSet rs = stmt.executeQuery("SELECT * FROM sqlaction_demo") ; while( rs.next() ) { SqlactionDemoSAO sqlactionDemo = new SqlactionDemoSAO() ; sqlactionDemo.id = rs.getInt( 1 ) ; sqlactionDemo.name = rs.getString( 2 ) ; sqlactionDemo.address = rs.getString( 3 ) ; sqlactionDemoListForSelectOutput.add(sqlactionDemo) ; } return sqlactionDemoListForSelectOutput.size(); } // SELECT * FROM sqlaction_demo WHERE name=? public static int SELECT_ALL_FROM_sqlaction_demo_WHERE_name_E_( Connection conn, List<SqlactionDemoSAO> sqlactionDemoListForSelectOutput, String _1_name ) throws Exception { PreparedStatement prestmt = conn.prepareStatement("SELECT * FROM sqlaction_demo WHERE name=?") ; prestmt.setString( 1, _1_name ); ResultSet rs = prestmt.executeQuery() ; while( rs.next() ) { SqlactionDemoSAO sqlactionDemo = new SqlactionDemoSAO() ; sqlactionDemo.id = rs.getInt( 1 ) ; sqlactionDemo.name = rs.getString( 2 ) ; sqlactionDemo.address = rs.getString( 3 ) ; sqlactionDemoListForSelectOutput.add(sqlactionDemo) ; } return sqlactionDemoListForSelectOutput.size(); } // INSERT INTO sqlaction_demo (name,address) VALUES (?,?) public static int INSERT_INTO_sqlaction_demo( Connection conn, SqlactionDemoSAO sqlactionDemo ) throws Exception { PreparedStatement prestmt = conn.prepareStatement("INSERT INTO sqlaction_demo (name,address) VALUES (?,?)") ; prestmt.setString( 1, sqlactionDemo.name ); prestmt.setString( 2, sqlactionDemo.address ); return prestmt.executeUpdate() ; } // UPDATE sqlaction_demo SET address=? WHERE name=? public static int updateAddressByName( Connection conn, String _1_address_ForSetInput, String _1_name_ForWhereInput ) throws Exception { PreparedStatement prestmt = conn.prepareStatement("UPDATE sqlaction_demo SET address=? WHERE name=? ") ; prestmt.setString( 1, _1_address_ForSetInput ); prestmt.setString( 2, _1_name_ForWhereInput ); return prestmt.executeUpdate() ; } // DELETE FROM sqlaction_demo WHERE name=? public static int DELETE_FROM_sqlaction_demo_WHERE_name_E_( Connection conn, String _1_name ) throws Exception { PreparedStatement prestmt = conn.prepareStatement("DELETE FROM sqlaction_demo WHERE name=?") ; prestmt.setString( 1, _1_name ); return prestmt.executeUpdate() ; } }
SqlactionDemoSAU.java
// This file generated by sqlaction v0.2.1.0 package xyz.calvinwilliams.sqlaction; import java.math.*; import java.util.*; import java.sql.Time; import java.sql.Timestamp; import java.sql.Connection; import java.sql.Statement; import java.sql.PreparedStatement; import java.sql.ResultSet; public class SqlactionDemoSAU { }
工具sqlaction
内部处理流程如下:
dbserver.conf.json
,如果没有找到就迭代往上级目录继续找,从该配置文件中获得数据库连接配置信息。然后以相同的查找逻辑读取sqlaction.conf.json
获得数据库名、表列表、以及每张表需要执行的SQL语句列表等信息。配置文件往上查找机制是为了灵活适应不同规模项目目录的规划。XxxSao.java
和用户自定义代码文件XxxSau.java
,,sqlaction配置文件中的每一条SQL自动解析生成基于JDBC的JAVA方法,从此再也不用手工写JDBC代码了!Demo.java
package xyz.calvinwilliams.sqlaction.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { Connection conn = null ; List<SqlactionDemoSAO> sqlactionDemoList = null ; SqlactionDemoSAO sqlactionDemo = null ; SqlactionDemoSAO sqlactionDemoForSetInput = null ; SqlactionDemoSAO sqlactionDemoForWhereInput = null ; int nret = 0 ; // Connect to database try { Class.forName( "com.mysql.jdbc.Driver" ); conn = DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/calvindb?serverTimezone=GMT", "calvin", "calvin" ) ; } catch (ClassNotFoundException e1) { e1.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } try { conn.setAutoCommit(false); // Delete records with name nret = SqlactionDemoSAO.DELETE_FROM_sqlaction_demo_WHERE_name_E_( conn, "Calvin" ) ; if( nret < 0 ) { System.out.println( "SqlactionDemoSAO.DELETE_FROM_sqlaction_demo_WHERE_name_E_ failed["+nret+"]" ); conn.rollback(); return; } else { System.out.println( "SqlactionDemoSAO.DELETE_FROM_sqlaction_demo_WHERE_name_E_ ok , rows["+nret+"] effected" ); } // Insert record sqlactionDemo = new SqlactionDemoSAO() ; sqlactionDemo.name = "Calvin" ; sqlactionDemo.address = "My address" ; nret = SqlactionDemoSAO.INSERT_INTO_sqlaction_demo( conn, sqlactionDemo ) ; if( nret < 0 ) { System.out.println( "SqlactionDemoSAO.INSERT_INTO_sqlaction_demo failed["+nret+"]" ); conn.rollback(); return; } else { System.out.println( "SqlactionDemoSAO.INSERT_INTO_sqlaction_demo ok" ); } // Update record with name nret = SqlactionDemoSAO.updateAddressByName( conn, "My address 2", "Calvin" ) ; if( nret < 0 ) { System.out.println( "SqlactionDemoSAO.updateAddressByName failed["+nret+"]" ); conn.rollback(); return; } else { System.out.println( "SqlactionDemoSAO.updateAddressByName ok , rows["+nret+"] effected" ); } // Query records sqlactionDemoList = new LinkedList<SqlactionDemoSAO>() ; nret = SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo( conn, sqlactionDemoList ) ; if( nret < 0 ) { System.out.println( "SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo failed["+nret+"]" ); conn.rollback(); return; } else { System.out.println( "SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo ok" ); for( SqlactionDemoSAO r : sqlactionDemoList ) { System.out.println( " id["+r.id+"] name["+r.name+"] address["+r.address+"]" ); } } conn.commit(); } catch(Exception e) { try { conn.rollback(); } catch (Exception e2) { return; } } return; } }
SqlactionDemoSAO.DELETE_FROM_sqlaction_demo_WHERE_name_E_ ok , rows[1] effected SqlactionDemoSAO.INSERT_INTO_sqlaction_demo ok SqlactionDemoSAO.UPDATE_sqlaction_demo_SET_address_E_WHERE_name_E_ ok , rows[1] effected SqlactionDemoSAO.SELECT_ALL_FROM_sqlaction_demo ok id[15] name[Calvin] address[My address 2]
总结:
对表的增删改查只需调用前面自动生成的数据库表实体类中的方法即可,而且底层执行代码可随时查看,没有什么秘密,没有什么高深的技术。
工具sqlaction
只在开发阶段使用,与运行阶段无关,说到底只是在应用与JDBC之间自动生成了薄薄的一层代码而已,把大量手工冗余代码都自动生成了,让开发者节省大量时间而去关注业务,减少大量机械操作减轻心智负担,提高生产力,早点做完工作回家抱女盆友/老婆 :)
欢迎使用sqlaction
,如果你在使用中碰到了问题请告诉我,谢谢 ^_^
Apache Maven
<dependency> <groupId>xyz.calvinwilliams</groupId> <artifactId>sqlaction</artifactId> <version>0.2.1.0</version> </dependency>
Gradle Kotlin DSL
compile("xyz.calvinwilliams:sqlaction:0.2.1.0")
厉华,右手C,左手JAVA,写过小到性能卓越方便快捷的日志库、HTTP解析器、日志采集器等,大到交易平台/中间件等,分布式系统实践者,容器技术专研者,目前在某城商行负责基础架构。
问题内容: 我有一个带有id(guid)列的sql表。如何强制数据库为每条新记录自动生成新的Guid? 问题答案: 添加。
问题内容: 我正在寻找一种基于类中定义的字段为现有Java源代码文件中的新方法自动生成源代码的方法。 本质上,我希望执行以下步骤: 读取并解析 遍历源代码中定义的所有字段 添加源代码方法 保存(理想情况下,保留现有代码的格式) 哪些工具和技术最适合完成此任务? 编辑 我不想在运行时生成代码;我想扩充现有的Java 源代码 问题答案: 用自动生成的代码修改相同的Java源文件是维护的噩梦。考虑生成一
为了方便一部分人还是做了一个代码生成器的,但我本人不喜欢用代码生成器,除非项目比较庞大的时候。 代码生成器的代码在 codegen 这里,它是一个maven工程, 你只需要下载下来修改一下配置就可以了: public static void main(String[] args) throws IOException { DBMeta dbMeta = new DBMeta()
我正试图用我的MERN stack web应用程序自动化构建过程。 目前,我使用CodePipeline,它: 从GitHub获取我的代码作为源代码 使用CodeBuild(Ubuntu 2.0)运行构建 并将其部署到我的Elastic BeanStalk环境中 步骤1 在尝试使用CodeBuild之后,即使客户端似乎完全按照日志进行构建,前端似乎也不会更新。 以下是我CodeBuild项目的一些
我们正在改变数据库,从一个支持8位int的数据库到一个不支持8位int的数据库。当Liquibase创建一个DB导致jOOQ生成“短”变量时,我们的代码会中断,但我们的代码使用字节/字节-这会中断代码签名。 有人建议我们继续使用以前的数据库(HSQLDB)来生成代码,而不是重新编码,它“应该”与新数据库一起运行。有不同的意见,除了直觉,我找不到任何决定性的东西,这似乎与jOOQ的设计目的背道而驰。
问题内容: 是否有可用于Eclipse或Netbeans或任何其他IDE的插件,可以通过直接从数据库中读取模式来帮助我生成hbm.xml。 编写这些XML似乎是不必要的时间浪费,因此应该自动进行。 问题答案: 对于NetBeans,请参阅在Web应用程序中使用hibernate。 对于Eclipse,请使用Hibernate Tools 插件。这是一个演示如何使用其 逆向工程 功能的教程。
问题内容: 我在apache和mod_wsgi和PostgreSQL中使用django(都在同一主机上),并且我需要处理很多简单的动态页面请求(每秒数百个)。我面临的问题是,瓶颈是django没有持久的数据库连接,并在每个请求上重新连接(大约需要5毫秒)。在进行基准测试时,我得到了持久连接,我可以处理近500 r / s,而没有连接时,我只能得到50 r / s。 有人有什么建议吗?如何修改Dja