厌烦了MyBatis和JPA(Hibernate)的冗余配置和繁琐使用,以及XML拷来拷去,写那么多Mapper要是直接用JDBC早就写完了,如果使用一个框架/工具比不使用而带来更多的心智负担,那还不如不用。其实,直接使用JDBC还是蛮干净和高性能的,只要解决其三个痛点:
MyBatis
JPA
Hibernate
能否造一个更好的轮子?
于是,我花了十多个晚上,结合之前在C技术栈中的设计和经验,结合JAVA特点,写了sqlaction。
sqlaction是自动生成JDBC代码的数据库持久层工具,它为应用提供了类似MyBatis和Hibernate操作数据库能力,但更轻量级和几乎消除了所有的手工冗余编码,提高开发效率,也易于与其它框架搭配使用。sqlaction读取数据库中的表结构元信息和少量配置文件信息(SQL),自动生成数据库表实体类,自动生成基于JDBC的SQL动作方法代码,自动生成拦截器框架等代码,应用调用其自动生成的代码就能极其快捷的操作数据库,同时还拥有JDBC的高性能,更重要的是开发者能直接看到底层操作代码,增加自主可控,没有低效的反射,没有复杂的热修改字节码,没有庞大笨重的隐藏核心。
sqlaction
放一个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。
okjson
mysql-connector- java-X.Y.Z.jar
okjson-0.0.9.0.jar
建立包目录,在包目录或上级某一级目录中创建数据库连接配置文件dbserver.conf.json,工具会从执行目录开始往上查找,只要某一级目录中存在这个配置文件即可。
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,工具会从执行目录开始往上查找,只要某一级目录中存在这个配置文件即可。
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" }
我把执行命令行包成批处理文件后执行,欢迎懂Eclipse插件开发的同学帮我写个插件 :)
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依赖声明见最后面“关于本项目”章节。
Maven
maven
C:\User\用户名\.m2\repository\xyz\calvinwilliams\
执行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源代码文件
*** ERROR : ...
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内部处理流程如下:
XxxSao.java
XxxSau.java
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,如果你在使用中碰到了问题请告诉我,谢谢 ^_^
源码托管地址 : Gitee、github
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解析器、日志采集器等,大到交易平台/中间件等,分布式系统实践者,容器技术专研者,目前在某城商行负责基础架构。
通过邮箱联系我 : 网易、Gmail