dromara / easy-query

java/kotlin high performance lightweight solution for jdbc query,support oltp and olap query,一款java下面支持强类型、轻量级、高性能的ORM,致力于解决jdbc查询,拥有对象模型筛选、隐式子查询、隐式join

Home Page:http://www.easy-query.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

大神可以添加动态建表建库吗?

roy00008 opened this issue · comments

因为有些业务需要到动态建表,建库呢

@roy00008 原则上是可以的目前已sql的写法可以做到动态表和分表

image

demo已经上传,这个对象的这个路由支持你传入任意时间他会创建任意表名,并且支持复杂查询

这个demo在哪里呀 大神

大神按你的写法,我这边运行调用的就报错呢。

[ERROR][11-15 10:11:57.234][jlhttp-1][*][c.e.q.c.u.EasyJdbcExecutorUtil.insert - 260]: INSERT INTO lottery (lottery_num,lottery_result,lottery_type,table_key) VALUES (?,?,?,?)
java.sql.BatchUpdateException: Table 'lucky-game-lottery.lottery' doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.cj.util.Util.handleNewInstance(Util.java:192)
at com.mysql.cj.util.Util.getInstance(Util.java:167)
at com.mysql.cj.util.Util.getInstance(Util.java:174)
at com.mysql.cj.jdbc.exceptions.SQLError.createBatchUpdateException(SQLError.java:224)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchWithMultiValuesClause(ClientPreparedStatement.java:718)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchInternal(ClientPreparedStatement.java:409)
at com.mysql.cj.jdbc.StatementImpl.executeBatch(StatementImpl.java:795)
at com.zaxxer.hikari.pool.ProxyStatement.executeBatch(ProxyStatement.java:128)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeBatch(HikariProxyPreparedStatement.java)
at com.easy.query.core.util.EasyJdbcExecutorUtil.insert(EasyJdbcExecutorUtil.java:226)
at com.easy.query.core.basic.jdbc.executor.internal.unit.impl.EasyInsertExecutor.executeCommandUnit(EasyInsertExecutor.java:44)
at com.easy.query.core.basic.jdbc.executor.internal.unit.impl.EasyInsertExecutor.executeCommandUnit(EasyInsertExecutor.java:27)
at com.easy.query.core.basic.jdbc.executor.internal.unit.abstraction.AbstractExecutor.groupExecute(AbstractExecutor.java:117)
at com.easy.query.core.basic.jdbc.executor.internal.unit.abstraction.AbstractExecutor.executeSingle0(AbstractExecutor.java:75)
at com.easy.query.core.basic.jdbc.executor.internal.unit.abstraction.AbstractExecutor.execute0(AbstractExecutor.java:67)
at

@roy00008 看你的错误应该是使用了schema,所以你先确定下创建表的时候有没有指定schema就是lucky-game-lottery比如原来是

create table lottery(....)
应该改成
create table lucky-game-lottery.lottery(....)

如果已经在create table上添加了schema那么应该是你的表没有添加schema

@Table(value = "lottery",schema = "lucky-game-lottery")
 private synchronized void addShardingTail(String tail) {
        if (!tails.containsKey(tail)) {

            try {
                String sql = "create table order_test_" + tail + "\n" +
                        "(\n" +
                        "    id        varchar(128) not null\n" +
                        "        primary key,\n" +
                        "    name       varchar(128)          not null,\n" +
                        "    create_time   datetime not null\n" +
                        ");";
                easyQuery.sqlExecute(sql);
            } catch (Exception ex) {
                log.error("创建表失败:" + ex.getMessage(), ex);
            }

            EasyQueryOption easyQueryOption = easyQuery.getRuntimeContext().getQueryConfiguration().getEasyQueryOption();
            EntityMetadata entityMetadata = easyQuery.getRuntimeContext().getEntityMetadataManager().getEntityMetadata(entityClass());
            entityMetadata.addActualTableWithDataSource(easyQueryOption.getDefaultDataSourceName(), entityMetadata.getTableName() + tableSeparator() + tail);
            tails.put(tail, def);
        }
    }

添加了schema还是会出错。

什么错误呢
@roy00008

java.sql.BatchUpdateException: Table 'lucky-game-lottery.lottery' doesn't exist
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.cj.util.Util.handleNewInstance(Util.java:192)
at com.mysql.cj.util.Util.getInstance(Util.java:167)
at com.mysql.cj.util.Util.getInstance(Util.java:174)
at com.mysql.cj.jdbc.exceptions.SQLError.createBatchUpdateException(SQLError.java:224)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchWithMultiValuesClause(ClientPreparedStatement.java:718)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchInternal(ClientPreparedStatement.java:409)
at com.mysql.cj.jdbc.StatementImpl.executeBatch(StatementImpl.java:795)

package com.example.data.sharding;

import com.easy.query.api4j.client.EasyQuery;
import com.easy.query.core.configuration.EasyQueryOption;
import com.easy.query.core.logging.Log;
import com.easy.query.core.logging.LogFactory;
import com.easy.query.core.metadata.EntityMetadata;
import com.easy.query.core.sharding.api.route.time.AbstractMonthTableRoute;
import com.easy.query.solon.annotation.Db;
import com.example.data.entity.LotteryDa;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**

  • 赛果分表片区
    */
    @component
    public class LotteryTableRoute extends AbstractMonthTableRoute {
    private static final Log log = LogFactory.getLog(LotteryTableRoute.class);
    public static final ConcurrentHashMap<String, Object> tails = new ConcurrentHashMap<>();
    private final Object def = new Object();
    private boolean inited = false;

    @db("main-db")
    private EasyQuery easyQuery;
    @OverRide
    protected LocalDateTime convertLocalDateTime(Object o) {
    return (LocalDateTime) o;
    }

    @OverRide
    protected String formatShardingValue(LocalDateTime time) {
    String shardingValue = time.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    if (!inited) {
    initTails();
    }
    checkShardingTail(shardingValue);
    return shardingValue;
    }

    private synchronized void initTails() {
    if (!inited) {
    initTails0();
    inited = true;
    }
    }

    private void initTails0() {
    EntityMetadata entityMetadata = easyQuery.getRuntimeContext().getEntityMetadataManager().getEntityMetadata(entityClass());
    EasyQueryOption easyQueryOption = easyQuery.getRuntimeContext().getQueryConfiguration().getEasyQueryOption();
    List<Map<String, Object>> maps = easyQuery.sqlQueryMap("SHOW TABLES");
    List tables = maps.stream().flatMap(o -> o.values().stream()).map(o -> o.toString())
    .filter(o -> o.startsWith("lottery")).collect(Collectors.toList());
    for (String table : tables) {
    entityMetadata.addActualTableWithDataSource(easyQueryOption.getDefaultDataSourceName(), table);
    tails.put(table.substring(8), def);
    }
    }

    private void checkShardingTail(String tail) {
    if (!tails.containsKey(tail)) {
    addShardingTail(tail);
    }
    }

    private synchronized void addShardingTail(String tail) {
    if (!tails.containsKey(tail)) {
    try {
    String sql = "CREATE TABLE lottery(\n" +
    " ID INT NOT NULL AUTO_INCREMENT COMMENT 'ID ID' ,\n" +
    " LOTTERY_NUM BIGINT NOT NULL COMMENT '期数 期数' ,\n" +
    " LOTTERY_RESULT VARCHAR(1024) NOT NULL COMMENT '赛果 赛果' ,\n" +
    " LOTTERY_TYPE INT NOT NULL COMMENT '票种 100=澳8;200=澳5' ,\n" +
    " TABLE_KEY VARCHAR(32) NOT NULL COMMENT '表键值' ,\n" +
    " REVISION INT COMMENT '乐观锁' ,\n" +
    " CREATED_TIME DATETIME COMMENT '创建时间' ,\n" +
    " UPDATED_TIME DATETIME COMMENT '更新时间' ,\n" +
    " PRIMARY KEY (ID)\n" +
    ");";
    easyQuery.sqlExecute(sql);
    } catch (Exception ex) {
    log.error("创建表失败:" + ex.getMessage(), ex);
    }

         EasyQueryOption easyQueryOption = easyQuery.getRuntimeContext().getQueryConfiguration().getEasyQueryOption();
         EntityMetadata entityMetadata = easyQuery.getRuntimeContext().getEntityMetadataManager().getEntityMetadata(entityClass());
         entityMetadata.addActualTableWithDataSource(easyQueryOption.getDefaultDataSourceName(), entityMetadata.getTableName() + tableSeparator() + tail);
         tails.put(tail, def);
     }
    

    }
    }

package com.example.data.entity;

import com.easy.query.core.annotation.Column;
import com.easy.query.core.annotation.Table;
import com.easy.query.core.annotation.Version;
import com.easy.query.core.basic.extension.version.VersionLongStrategy;
import lombok.Data;

@table(value = "lottery",schema = "lucky-game-lottery")
@DaTa
public class LotteryDa {
/** ID;ID /
@column(primaryKey = true)
private Integer id ;
/
* 期数;期数 /
private Long lotteryNum ;
/
* 赛果;赛果 /
private String lotteryResult ;
/
* 票种;100=澳8;200=澳5 /
private Integer lotteryType ;
/
* 表键值 /
private String tableKey ;
/
* 乐观锁 */
@Version(strategy = VersionLongStrategy.class)
private Integer revision ;
}

package com.example.controller;

import com.easy.query.api.proxy.client.EasyProxyQuery;
import com.easy.query.solon.annotation.Db;
import com.example.data.entity.LotteryDa;
import com.example.data.sharding.LotteryTableRoute;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.cache.jedis.RedisCacheService;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.Result;
import org.noear.solon.core.handle.SessionState;

import java.util.ArrayList;

@controller
public class TestController {

@Inject("${youcan.cache}")
private RedisCacheService redissonClient;

@Db("main-db")
private EasyProxyQuery easyProxyQuery;

@Inject
private LotteryTableRoute lotteryTableRoute;



@Mapping(value = "/get/{key}")
private Result get(Context ctx,String key){
    ArrayList<LotteryDa> orderEntities = new ArrayList<>();
    LotteryDa lotteryDa = new LotteryDa();
    lotteryDa.setLotteryNum(31094502l);
    lotteryDa.setLotteryType(100);
    lotteryDa.setLotteryResult("9,1,6,20,13,16,14,12");
    lotteryDa.setTableKey("20231115");
    orderEntities.add(lotteryDa);
    easyProxyQuery.insertable(orderEntities).executeRows();
    return Result.succeed(redissonClient.get(key,String.class));
}

}

@roy00008

create table lottery(....)
应该改成
create table lucky-game-lottery.lottery(....)
String sql = "CREATE TABLE lottery(\n" + --这边把schema加上我之前说了
" ID INT NOT NULL AUTO_INCREMENT COMMENT 'ID ID' ,\n" +
" LOTTERY_NUM BIGINT NOT NULL COMMENT '期数 期数' ,\n" +
" LOTTERY_RESULT VARCHAR(1024) NOT NULL COMMENT '赛果 赛果' ,\n" +
" LOTTERY_TYPE INT NOT NULL COMMENT '票种 100=澳8;200=澳5' ,\n" +
" TABLE_KEY VARCHAR(32) NOT NULL COMMENT '表键值' ,\n" +
" REVISION INT COMMENT '乐观锁' ,\n" +
" CREATED_TIME DATETIME COMMENT '创建时间' ,\n" +
" UPDATED_TIME DATETIME COMMENT '更新时间' ,\n" +
" PRIMARY KEY (ID)\n" +
");";
image

而且你好像没有用到tail这个属性,demo里面我是进行了分表处理

我想问一下 LotteryTableRoute 这个类是在哪调用的?

因为我的代码里面都没有地方调用 LotteryTableRoute 所以我就很奇怪

调用这句 easyProxyQuery.insertable(orderEntities).executeRows(); 框架是怎么知道先去调用LotteryTableRoute创建表的

@component 注解会将其注入到默认的easy-query框架内部

image 框架会获取到route路由然后添加到 TableRouteManager里面在执行的时候会判断当前是否是分片对象如果是并且是分表对象那么就会去获取对应的路由然后执行响应的方法,(在格式化尾巴后缀的时候判断这个后缀是否已经被创建了没有就创建表 **这部分就是你自己实现的代码**)

@roy00008 执行逻辑你可以打这个断点 EntityExpressionExecutor 实现类是 DefaultEntityExpressionExecutor

LotteryTableRoute 这个类我打了断点都没有进去的

@roy00008 你的easy-query是starter构建的吗还是自己构建的bean如果是自己构建的需要自己添加route到tableRouteManager

 TableRouteManager tableRouteManager = runtimeContext.getTableRouteManager();
        tableRouteManager.addRoute(new TopicShardingTableRoute());

可以直接看单元测试

https://github.com/xuejmnet/easy-query/blob/f89fa54629d38875fc503e075be884837765dd64/sql-test/src/main/java/com/easy/query/test/BaseTest.java#L179

starter构建的bean会根据@component注解自己去注册,本质还是tableRouteManager.addRoute(new TopicShardingTableRoute());

import org.noear.solon.annotation.Component;

会不会是import org.noear.solon.annotation.Component; 这个的识别不了

@roy00008 你是solon吗,如果是solon那么看文档里面的 https://xuejm.gitee.io/easy-query-doc/guide/config/config-solon.html#solon%E6%89%80%E6%9C%89%E9%85%8D%E7%BD%AE

//    /**
//     * 添加分表或者分库的路由,分库数据源
//     * @param runtimeContext
//     */
//    @Bean
//    public void db1QueryRuntimeContext(@Db("db1") QueryRuntimeContext runtimeContext){
//        TableRouteManager tableRouteManager = runtimeContext.getTableRouteManager();
//        DataSourceRouteManager dataSourceRouteManager = runtimeContext.getDataSourceRouteManager();
//        tableRouteManager.addRoute(...);
//        dataSourceRouteManager.addRoute(...);
//
//        DataSourceManager dataSourceManager = runtimeContext.getDataSourceManager();
//
//        dataSourceManager.addDataSource(key, dataSource, poolSize);
//    }

solon的操作都需要手动添加,因为solon支持多数据源,如果只是@component那么无法知晓具体到哪个数据源所以都是手动加的

@bean
public void db1QueryRuntimeContext(@db("main-db") QueryRuntimeContext runtimeContext){
TableRouteManager tableRouteManager = runtimeContext.getTableRouteManager();
tableRouteManager.addRoute(new LotteryTableRoute());
}

写成这样以后,还是提示Table 'lucky-game-lottery.lottery' doesn't exist

大神,应该怎么写吖,能不能用solon写一个demo,可以动态建表的

如果可以了的话我就关闭了 : )