前两天在网上看到一篇文章,说java8不再需要ORM了。看了以后 ,觉得还可以,再次做一下分享。
首先,我要对ORM做一些吐槽,以hibernate为代表的ORM简直见识开发者的噩梦呀。本人对此深恶痛绝。什么one-to-many, many-to-many ,搞死人不说,还特别麻烦,该死的延迟加载等等,用起来就是非常不爽。这里我还是觉得MyBatis靠谱一点。很早就想吐槽Hibernate的,一直没机会。
不需要ORM,java8提供了什么东东呢?那就是流式api以及函数式编程。
来看一个简单的例子,这里使用H2(我超喜欢H2,绝对是个好东西呀~~~),访问H2的系统表INFORMATION_SCHEMA ,去收集表和列的关系。
数据结构使用 Map<String, List<String>>,使用jOOQ来操作SQL。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public
static
void main
(
String
[
] args
)
throws Exception { Class. forName ( "org.h2.Driver" ) ; try ( Connection c = getConnection ( "jdbc:h2:~/sql-goodies-with-mapping", "sa", "" ) ) { // This SQL statement produces all table // names and column names in the H2 schema String sql = "select table_name, column_name " + "from information_schema.columns " + "order by " + "table_catalog, " + "table_schema, " + "table_name, " + "ordinal_position" ; // This is jOOQ's way of executing the above // statement. Result implements List, which // makes subsequent steps much easier Result result = DSL. using (c ) . fetch (sql ) } } |
产生处理结果:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
DSL.
using
(c
)
. fetch (sql ) . stream ( ) . collect (groupingBy ( r -> r. getValue ( "TABLE_NAME" ), mapping ( r -> r. getValue ( "COLUMN_NAME" ), toList ( ) ) ) ) . forEach ( (table, columns ) -> System. out. println (table + ": " + columns ) ) ; |
输出如下:
1
2 3 |
FUNCTION_COLUMNS: [ALIAS_CATALOG, ALIAS_SCHEMA, ...]
CONSTANTS: [CONSTANT_CATALOG, CONSTANT_SCHEMA, ...] SEQUENCES: [SEQUENCE_CATALOG, SEQUENCE_SCHEMA, ...] |
它的工作过程如下的注释:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
DSL.
using
(c
)
. fetch (sql ) //将List转为流 . stream ( ) // 将流内的内容转入另外一个集合 . collect ( // 用group操作产生一个Map groupingBy ( // 指定Group操作的key r -> r. getValue ( "TABLE_NAME" ), // 这个mapping返回group操作的内容 mapping ( // 我要的值就是表的列名啦 r -> r. getValue ( "COLUMN_NAME" ), // 将收集到的东东转为List toList ( ) ) ) ) // 得到集合后,用lambda 表达式输出 . forEach ( (table, columns ) -> System. out. println (table + ": " + columns ) ) ; |
以上代码直接用流式表达式处理了SQL的返回。当然,这是有些革命性的变化,如果你不喜欢java这么写,理解起来要花点时间,
但是,习惯的话,应该还好。
这里注意一下,以上代码有一个static import
1
|
import
static java.
util.
stream.
Collectors.
*;
|
还有一个问题,就是排序,因为我们用了HashMap,如果需要排序就可以用 LinkedHashMap
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
DSL.
using
(c
)
. fetch (sql ) . stream ( ) . collect (groupingBy ( r -> r. getValue ( "TABLE_NAME" ), // Add this Supplier to the groupingBy // method call LinkedHashMap :: new, mapping ( r -> r. getValue ( "COLUMN_NAME" ), toList ( ) ) ) ) . forEach (... ) ; |
LinkedHashMap的特点以及与HashMap的区别,大家可以参考这本书看看。
更进一步,让我们看看如何根据数据库的信息,来产生对应的DDL脚本。首先,我们要取得我们需要的信息,列明,数据类型云云。
1
2 3 4 5 6 7 8 9 10 11 |
String sql
=
"select " + "table_name, " + "column_name, " + "type_name " + // Add the column type "from information_schema.columns " + "order by " + "table_catalog, " + "table_schema, " + "table_name, " + "ordinal_position" ; |
引入一个类,来包装一下,
1
2 3 4 5 6 7 8 9 |
class Column
{
final String name ; final String type ; Column ( String name, String type ) { this. name = name ; this. type = type ; } } |
改一下我们的流式代码,让他生成DDL
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
result
. stream ( ) . collect (groupingBy ( r -> r. getValue ( "TABLE_NAME" ), LinkedHashMap :: new, mapping ( // 我们要包装到我们自己的类里去了哦~~~ r -> new Column ( r. getValue ( "COLUMN_NAME", String. class ), r. getValue ( "TYPE_NAME", String. class ) ), toList ( ) ) ) ) . forEach ( (table, columns ) -> { // Just emit a CREATE TABLE statement System. out. println ( "CREATE TABLE " + table + " (" ) ; System. out. println ( columns. stream ( ) . map (col -> " " + col. name + " " + col. type ) . collect (Collectors. joining ( ",\n" ) ) ) ; System. out. println ( ");" ) ; } ) ; |
以下就是程序的输出,完美的DDL脚本:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
CREATE
TABLE CATALOGS
(
CATALOG_NAME VARCHAR ); CREATE TABLE COLLATIONS ( NAME VARCHAR , KEY VARCHAR ); CREATE TABLE COLUMNS ( TABLE_CATALOG VARCHAR , TABLE_SCHEMA VARCHAR , TABLE_NAME VARCHAR , COLUMN_NAME VARCHAR , ORDINAL_POSITION INTEGER , COLUMN_DEFAULT VARCHAR , IS_NULLABLE VARCHAR , DATA_TYPE INTEGER , CHARACTER_MAXIMUM_LENGTH INTEGER , CHARACTER_OCTET_LENGTH INTEGER , NUMERIC_PRECISION INTEGER , NUMERIC_PRECISION_RADIX INTEGER , NUMERIC_SCALE INTEGER , CHARACTER_SET_NAME VARCHAR , COLLATION_NAME VARCHAR , TYPE_NAME VARCHAR , NULLABLE INTEGER , IS_COMPUTED BOOLEAN , SELECTIVITY INTEGER , CHECK_CONSTRAINT VARCHAR , SEQUENCE_NAME VARCHAR , REMARKS VARCHAR , SOURCE_DATA_TYPE SMALLINT ); |