Orm至簡化框架:Neo

Neo是一個基於JDBC開發的至簡化框架,名字源於《黑客帝國》男主角名字,寓意爲連接虛擬與現實。開發源頭,源於之前接觸的一些ORM框架,思想很不錯但是沒開源無法使用,而且自己也有很多想法因此設計了該框架。

使用文檔

Neo文檔介紹
最新Neo文檔介紹

快速入門

該框架秉承大道至簡理念,採用一個Neo對象對應一個DataSource方式,然後這個Neo對象擁有對錶的各種操作。

maven引入

當前已經發布到maven中央倉庫,直接使用即可,目前最低版本0.3.0,不同版本的api差距不小,建議使用最新版本。目前版本還未脫離實驗階段,如果有什麼問題請及時反饋

<dependency>
  <groupId>com.github.simonalong</groupId>
  <artifactId>Neo</artifactId>
 <!-- 請替換具體的版本-->
  <version>${latest.release.version}</version>
</dependency>

快速入門

一個DB對應的一個對象Neo,操作表,則填入對應的表名即可

public void testDemo1() {
    String url = "jdbc:mysql://127.0.0.1:3306/neo?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
    String user = "neo_test";
    String password = "neo@Test123";
    String tableName = "neo_table1";
    // 連接
    Neo neo = Neo.connect(url, user, password);

    // 插入
    NeoMap data = neo.insert(tableName, NeoMap.of("group", "value"));

    data.put("group", "value1");

    // 更新
    neo.update(tableName, data);

    // 刪除
    neo.delete(tableName, data);

    // 查詢一行
    neo.one(tableName, data);

    // 查詢多行
    neo.list(tableName, data);

    // 查詢指定列的一個值
    neo.value(tableName, "group", data);

    // 查詢指定列的多個值
    neo.values(tableName, "group", data);

    // 查詢分頁
    neo.page(tableName, data, NeoPage.of(1, 20));
    
    // 分頁個數
    table.count(tableName, data);

    // 執行sql
    neo.execute("select * from %s where `group` =?", tableName, "group1");

    // 事務
    neo.tx(()->{
        neo.update(tableName, NeoMap.of("id", 12, "group", "value1"));
        neo.one(tableName, 12);
        neo.update("neo_table2", NeoMap.of("name", 12));
    });

    // 批量
    List<NeoMap> list = new ArrayList<>();
    list.add(NeoMap.of("group", "v1"));
    list.add(NeoMap.of("group", "v2"));
    list.add(NeoMap.of("group", "v3"));
    list.add(NeoMap.of("group", "v4"));
    neo.batchInsert(tableName, list);
    
    // 批量更新
    table.batchUpdate(tableName, list, Columns.of("group"));
}

指定表的話,就更簡單,一個表對應一個對象NeoTable

public void testDemo2() {
    String url = "jdbc:mysql://127.0.0.1:3306/neo?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
    String user = "neo_test";
    String password = "neo@Test123";
    String tableName = "neo_table1";
    // 連接
    Neo neo = Neo.connect(url, user, password);
    NeoTable table = neo.getTable(tableName);

    // 插入
    NeoMap data = table.insert(NeoMap.of("group", "value"));

    data.put("group", "value1");

    // 更新
    table.update(data);

    // 刪除
    table.delete(data);

    // 查詢一行
    table.one(data);

    // 查詢多行
    table.list(data);

    // 查詢指定列的一個值
    table.value("group", data);

    // 查詢指定列的多個值
    table.values("group", data);

    // 查詢分頁
    table.page(data, NeoPage.of(1, 20));
    
    // 分頁個數
    table.count(data);

    // 批量插入
    List<NeoMap> list = new ArrayList<>();
    list.add(NeoMap.of("group", "v1"));
    list.add(NeoMap.of("group", "v2"));
    list.add(NeoMap.of("group", "v3"));
    list.add(NeoMap.of("group", "v4"));
    table.batchInsert(list);
    
    // 批量更新
    table.batchUpdate(list, Columns.of("group"));
}

注意

生成一個Neo對象除了可以通過url、user和password,還可以通過DataSource方式

// 連接
Neo neo = Neo.connect(datasource);

指定實體

上面我們對數據的操作全都是基於map,下面我們基於實體DO對數據庫進行操作

/**
 * 指定表的話,就更簡單
 */
@Test
public void testDemo3() {
    String url = "jdbc:mysql://127.0.0.1:3306/neo?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
    String user = "neo_test";
    String password = "neo@Test123";
    String tableName = "neo_table1";
    // 連接
    Neo neo = Neo.connect(url, user, password);
    NeoTable table = neo.getTable(tableName);

    // 實體數據
    DemoEntity3 entity = new DemoEntity3().setGroup("group1").setUsName("name1");

    // 插入
    DemoEntity3 result = table.insert(entity);

    result.setUsName("name2");

    // 更新
    table.update(result);

    // 刪除
    table.delete(result);

    // 查詢一行
    table.one(result);

    // 查詢多行
    table.list(result);

    // 查詢指定列的
    table.value("group", NeoMap.of("user_name", "name2"));

    // 查詢指定列的多個值
    table.values("group", NeoMap.of("user_name", "name2"));

    // 查詢分頁,第一個參數是搜索條件
    table.page(NeoMap.of("user_name", "name2"), NeoPage.of(1, 20));
    
    // 分頁個數
    table.count(data);

    // 批量插入
    List<DemoEntity3> list = new ArrayList<>();
    list.add(new DemoEntity3().setGroup("group1").setUsName("name1"));
    list.add(new DemoEntity3().setGroup("group2").setUsName("name2"));
    list.add(new DemoEntity3().setGroup("group3").setUsName("name3"));
    list.add(new DemoEntity3().setGroup("group4").setUsName("name4"));
    table.batchInsertEntity(list);
    
    // 批量更新
    table.batchUpdateEntity(list, Columns.of("group"));
}

實體和DB字段映射

實體和DB中字段的映射,可以有三種方式進行配置

1.NeoMap全局配置:NeoMap.setDefaultNamingChg(xxx)

2.NeoMap實體單獨配置:neoMap.setNamingChg(xxx)

3.通過註解@Column對應配置:每個屬性上面添加@Column其中是DB中的屬性名

下面介紹@Column的用法,以後表字段修改時候,實體就不用修改,如果有屬性上面沒有添加註解,則默認按照類型NamingChg.UNDERLINE進行轉換

/**
 * @author robot
 */
@Data
@Accessors(chain = true)
public class NeoTable1DO {

    @Column("id")
    private Long id;

    /**
     * 數據來源組,外鍵關聯lk_config_group
     */
    @Column("group")
    private String group;

    /**
     * 任務name
     */
    @Column("name")
    private String name;

    /**
     * 修改人名字
     */
    @Column("user_name")
    private String userName;
    @Column("age")
    private Integer age;
    @Column("sl")
    private Long sl;
    @Column("data_name")
    private String dataName;
}

sql字段

CREATE TABLE `neo_table1` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `group` char(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '數據來源組,外鍵關聯lk_config_group',
  `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '任務name',
  `user_name` varchar(24) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '修改人名字',
  `age` int(11) DEFAULT NULL,
  `sl` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `group_index` (`group`)
) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

業務使用

也可以繼承使用,針對業務接入,可以直接繼承類AbstractBizService即可具備一個表的常見的所有功能,只需要實現如下兩個方法即可

public class BizServiceTest extends AbstractBizService {

    public BizServiceTest() throws SQLException {
    }

    @Override
    public DbSync getDb() {
        String url = "jdbc:mysql://127.0.0.1:3306/neo?useUnicode=true&characterEncoding=UTF-8&useSSL=false";
        String user = "neo_test";
        String password = "neo@Test123";
        return Neo.connect(url, user, password);
    }

    @Override
    public String getTableName() {
        return "neo_table1";
    }

    @Test
    public void testInsert() {
        TestEntity entity = new TestEntity()
            .setGroup("ok")
            .setUserName("me")
            .setName("hello");
        insert(entity);
    }
}

實體代碼生成器

本框架也支持實體生成器,如下,即可生成如上所示的類NeoTable1DO就是如下生成的

public class CodeGenTest {

    @Test
    public void test1(){
        EntityCodeGen codeGen = new EntityCodeGen()
            // 設置DB信息
            .setDb("neo_test", "neo@Test123", "jdbc:mysql://127.0.0.1:3306/neo?useUnicode=true&characterEncoding=UTF-8&useSSL=false")
            // 設置項目路徑
            .setProjectPath("/Users/zhouzhenyong/project/private/Neo")
            // 設置實體生成的包路徑
            .setEntityPath("com.simonalong.neo.entity")
            // 設置表前綴過濾
            // .setPreFix("t_")
            // 設置要排除的表
            //.setExcludes("xx_test")
            // 設置只要的表
            .setIncludes("neo_table1")
            // 設置屬性中數據庫列名字向屬性名字的轉換,這裏設置下劃線,比如:data_user_base -> dataUserBase
            .setFieldNamingChg(NamingChg.UNDERLINE);

        // 代碼生成
        codeGen.generate();
    }
}

分佈式ID生成器

我們這裏也提供了分佈式ID生成器方案,採用的是改進版雪花算法,徹底解決了雪花算法存在的常見問題(時間回撥問題,workerId回收問題),對於如何解決的,具體方案可見文檔,也可見我的另外一個項目Butterfly(Neo框架中的發號器方案是Butterfly中的一個使用選項)。

採用的是改進版雪花算法,不僅沒有時間回撥問題,性能還比雪花算法還要高十幾倍,普通機器QPS都可以達到1000w/s。

使用

先建表,如果沒有請創建

CREATE TABLE `neo_uuid_generator` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `namespace` varchar(128) DEFAULT '' COMMENT '命名空間',
  `work_id` int(16) NOT NULL DEFAULT '0' COMMENT '工作id',
  `last_expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '下次失效時間',
  `uid` varchar(128) DEFAULT '0' COMMENT '本次啓動唯一id',
  `ip` bigint(20) NOT NULL DEFAULT '0' COMMENT 'ip',
  `process_id` varchar(128) NOT NULL DEFAULT '0' COMMENT '進程id',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name_work` (`namespace`,`work_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@Test
public void generateTest1() {
    UuidGenerator generator = UuidGenerator.getInstance(neo);
    // 註冊(聲明)命令空間(在業務空間對應的集羣,最多可以有8192臺機器跑同一個業務,對大部分業務來說,足夠了)
    generator.addNamespaces("test1", "test2");
    
    System.out.println(generator.getUUid("test1"));
}

多表join

@Test
public void testJoin1() {
    // 首先獲取join的處理器,支持查詢one,list, value, values, page, count
    NeoJoiner neoJoiner = neo.joiner();

    // 配置的列
    Columns columns = Columns.of(neo);
    columns.table("neo_table1", "age");
    // 配置所有列,可以爲columns.table("neo_table2", "*")
    columns.table("neo_table2", "name", "group");

    // 配置多表join
    TableJoinOn tableJoinOn = new TableJoinOn("neo_table1");
    tableJoinOn.leftJoin("neo_table1", "neo_table2").on("id", "n_id");
    tableJoinOn.leftJoin("neo_table2", "neo_table3").on("n_id", "n_id");

    // 配置查詢條件
    TableMap searchMap = TableMap.of();
    searchMap.put("neo_table1", "name", "nihao");
    searchMap.put("neo_table2", "group", "ok");

    // select
    // neo_table1.`age` as neo_table1_dom_age,
    // neo_table2.`group` as neo_table2_dom_group,
    // neo_table2.`name` as neo_table2_dom_name
    //
    // from
    // neo_table1 left join neo_table2 on neo_table1.`id`=neo_table2.`n_id`
    // left join neo_table3 on neo_table2.`n_id`=neo_table3.`n_id`
    //
    // where neo_table2.`group` =  ? and neo_table1.`name` =  ?

    // [ok, nihao]
    show(neoJoiner.one(columns, tableJoinOn, searchMap));
}

異步

所有的api都有對應的異步api,列舉其中幾個接口api,接口太多,這裏不再列舉。其中線程池中的默認方式中,拒絕策略採用新的方式(重寫了拒絕策略),即:如果線程池全部都滿了,則任務阻塞在任務隊列中

    CompletableFuture<NeoMap> insertAsync(String tableName, NeoMap dataMap, Executor executor);

    CompletableFuture<NeoMap> insertAsync(String tableName, NeoMap dataMap);

    <T> CompletableFuture<T> insertAsync(String tableName, T object, Executor executor);

    <T> CompletableFuture<T> insertAsync(String tableName, T object);


    CompletableFuture<Integer> deleteAsync(String tableName, NeoMap dataMap, Executor executor);

    CompletableFuture<Integer> deleteAsync(String tableName, NeoMap dataMap);

    <T> CompletableFuture<Integer> deleteAsync(String tableName, T object, Executor executor);

    <T> CompletableFuture<Integer> deleteAsync(String tableName, T object);

    CompletableFuture<Integer> deleteAsync(String tableName, Number id, Executor executor);

    CompletableFuture<Integer> deleteAsync(String tableName, Number id);


    CompletableFuture<NeoMap> updateAsync(String tableName, NeoMap dataMap, NeoMap searchMap, Executor executor);

    CompletableFuture<NeoMap> updateAsync(String tableName, NeoMap dataMap, NeoMap searchMap);

    <T> CompletableFuture<T> updateAsync(String tableName, T setEntity, NeoMap searchMap, Executor executor);
...

更多功能

  • 數據庫連接
  • 基本功能
  • DB異步
  • 結構信息
  • 批量功能
  • 命名轉換
  • 事務
    • 單機事務
    • 分佈式XA事務(測試中)
  • sql監控
  • 主從
  • join
  • 實體代碼生成器
  • 分佈式
    • 全局id
  • 動態分庫分表(待支持)
  • 多數據源(待驗證)

技術討論羣:
請先加WX(zhoumo187108),並註明來源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章