Skip to content

DasClient 使用手册

Shengyuan 卢声远 edited this page Mar 1, 2021 · 5 revisions

简介

DasClient是用户操作数据库的统一接口。通过DasClient可以方便的对数据库进行增删改查。DasClient覆盖的功能范围和场景非常全面,既可以通过实体,也可以通过SQL进行ORM操作,同时还支持存储过程和事务

DasClient接口主要和Code Gen生成的实体,SqlBuilder配合使用。为在保持API简洁易用的情况下覆盖尽可能多的场景,每个接口可以使用Hints来支持同一类操作不同的变种。

DasClient支持透明的分库分表。绝大多数场景下用户体验和未分库时保持一致。极少数情况下当上下文无法判断如何分库分表的时候,可以通过Hints人工指定。 我们将给出常用的一些示例帮助你更方便使用DAS Client。

API说明

创建DasClient实例

DasClient的创建非常简单,只需提供逻辑数据库名即可。每个DasClient实例对应所有可以在该逻辑数据库上面进行的操作。实例是线程安全的对象,可以跨线程使用。

DasClient dao = DasClientFactory.getClient("logicDbName");

你也可以参考Sample spring boot工程详解中的代码,将DasClient配置成Spring bean。

数据库操作

获得指定逻辑数据库的dao后,即可对数据库进行操作。操作按照参数可以分三种类型:

  • 基于表实体的CRUD操作。包括单个实体的CRUD和多个实体的批量CRUD操作

  • 基于SQL builder的操作,具体包括:

    • SqlBuilder:构建任意的一条SQL语句

    • BatchUpdateBuilder:构建批量操作,分两类,一类是同一条语句,多组参数;一类是多不同条语句,无参数

    • CallBuilder:构建存储过程调用

    • BatchCallBuilder:构建对存储过程的批量调用

  • 事务操作。分为带返回值的事务和无返回值的事务操作 注意,Hints作为可选参数,可以不提供,但提供时,只能提供一个。由于Hints会存储操作的额外指令或操作的审计信息,因此Hints不推荐反复使用,请在需要使用的时候创建新的实例。

查询操作

按主键查询T queryByPk(T id, Hints...hints)方法

示例:

Person pk = new Person();
pk.setPeopleID(1);
pk = dao.queryByPk(pk); //返回主键为1的数据

按照给定样例进行查询queryBySample方法

示例:

Person sample = new Person();
sample .setName("test");
List<Person> plist = dao.queryBySample(sample );//返回name是"test"的数据

按照给定样例进行查询List<T> queryBySample(T sample, Hints...hints)方法

示例:

Person sample = new Person();
sample.setName("test");
List<Person> plist = dao.queryBySample(sample);//返回name是"test"的数据

按照给定样例进行分页查询List<T> queryBySample(T sample, PageRange range, Hints...hints)方法

示例:

Person sample = new Person();
sample.setName("test");
Person.PersonDefinition p = Person.PERSON;
 
//分页:每页10条,返回第1页数据(第1页是头一页)
List<Person> plist = dao.queryBySample(sample, PageRange.atPage(1, 10, p.PeopleID));
//分页:每页10条,返回第2页数据,按照CityID和CountryID升序
plist = dao.queryBySample(sample, PageRange.atPage(2, 10, p.CityID, p.CountryID), hints);
//分页:每页10条,返回第1页数据,按照PeopleID升序和CityID降序
plist = dao.queryBySample(sample, PageRange.atPage(1, 10, p.PeopleID.asc(), p.CityID.desc()), hints);

按照给定样例进行统计long countBySample(T sample, Hints...hints)方法

示例:

Person sample = new Person();
sample.setName("test");
long count = dao.countBySample(sample);//返回name是"test"的数据的条数

通过SqlBuilder查询数据库返回单条记录T queryObject(SqlBuilder builder)方法

示例:

Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.selectAllFrom(p)
    .where().allOf(p.PeopleID.eq(peopleId), p.CountryID.eq(countryID))
    .into(Person.class);
Person person = dao.queryObject(builder);

通过SqlBuilder查询数据库返回多条记录List<T> query(SqlBuilder builder)方法

示例:

Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.selectAllFrom(p)
    .where()
    .allOf(p.CountryID.eq(countryID).nullable(), p.CityID.eq(cityID))
    .orderBy(p.PeopleID.asc())
    .into(Person.class);
List<Person> plist = dao.query(builder);
//支持原生SQL
SqlBuilder builder = new SqlBuilder()
    .appendTemplate("select * from person where PeopleID = ?  AND CountryID = ?", Parameter.integerOf("", 3), Parameter.integerOf("", 4))
    .into(Person.class);
List<Person> plist = dao.query(builder);

通过BatchQueryBuilder批量的方式查询数据库返回多条查询语句的记录。List<?> batchQuery(BatchQueryBuilder builder)方法

示例:

BatchQueryBuilder batchBuilder = new BatchQueryBuilder();
Person.PersonDefinition p = Person.PERSON;
for (int j = 0; j < TABLE_MODE; j++) {
    List<Integer> pks = new ArrayList<>();
    for (int k = 0; k < TABLE_MODE; k++)
        pks.add(k + 1);
    SqlBuilder builder = SqlBuilder.selectAllFrom(p)
        .where()
        .allOf(p.PeopleID.in(pks), p.CountryID.eq(i), p.CityID.eq(j))
        .orderBy(p.PeopleID.asc())
        .into(Person.class);
    batchBuilder.addBatch(builder);
}
List<List<Person>> plist = (List<List<Person>>)dao.batchQuery(batchBuilder);

查询记录条数或单个记录T queryObject(SqlBuilder builder)方法

示例:

Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.selectCount().from(p).intoObject();
Number count = dao.queryObject(builder);

利用SqlBuilder分页查询

利用limit的MySQL示例:

Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.selectAllFrom(p)
    .orderBy(p.PeopleID.asc())
    .into(Person.class)
    .limit(2, 10);
List<Person> plist = dao.query(builder);

利用topoffset的SQLServer示例:

//use OFFSET
Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.selectAllFrom(p)
    .orderBy(p.PeopleID.asc())
    .into(Person.class)
    .offset(2, 10);
List<Person> plist = dao.query(builder);
 
//use TOP
SqlBuilder builder2 = SqlBuilder.selectAllFrom(p)
    .orderBy(p.PeopleID.asc())
    .into(Person.class)
    .top(10);
List<Person> plist2 = dao.query(builder2);

插入操作

插入一条记录int insert(T entity, Hints...hints)方法

示例:

Person person = new Person();
person.setName("jerry");
person.setCountryID(1);
person.setCityID(2);
dao.insert(person);

//设置主键后插入
person.setPeopleID(1);
dao.insert(person, Hints.hints().insertWithId());

//person对象插入后id会被填充
dao.insert(person, Hints.hints().setIdBack());

插入多条记录int insert(List<T> entities, Hints...hints)方法

示例:

List<Person> pl = new ArrayList<>();
for(int k = 0; k < 4; k++) {
    Person p = new Person();
    p.setName("jerry");
    p.setCountryID(k);
    p.setCityID(k);
    pl.add(p);
}
assertEquals(4, dao.insert(pl));

批处理方式插入多条记录int[] batchInsert(List<T> entities, Hints...hints)方法

示例:

List<Person> pl = new ArrayList<>();
for(int k = 0; k < 4; k++) {
    Person p = new Person();
    p.setName("jerry");
    p.setCountryID(k);
    p.setCityID(k);
    pl.add(p);
}
int[] ret = dao.batchInsert(pl);

更新操作

按主键查询更新记录int update(T entity, Hints...hints)方法

示例:

Person pk = new Person();
pk.setPeopleID(1);
pk.setName("Tom");
pk.setCountryID(1);
pk.setCityID(2);
assertEquals(1, dao.update(pk));

批处理方式更新多条记录int[] batchUpdate(List<T> entities, Hints...hints)方法

示例:

List<Person> pl = new ArrayList<>();
for (int k = 0; k < 4; k++) {
    Person pk = new Person();
    pk.setPeopleID(peopleId);
    pk.setName("Tom");
    pk.setCountryID(1);
    pk.setCityID(2);
    pl.add(pk);
}
int[] ret = dao.batchUpdate(pl);

通过SqlBuilder更新数据库int update(SqlBuilder builder)方法

示例:

Person.PersonDefinition p = Person.PERSON;
SqlBuilder builder = SqlBuilder.update(p)
    .set(p.Name.eq("Tom"), p.CountryID.eq(100), p.CityID.eq(200))
    .where(p.PeopleID.eq(1));
assertEquals(1, dao.update(builder));
 
SqlBuilder builder = SqlBuilder.deleteFrom(p)
    .where(p.PeopleID.eq(1));
 
SqlBuilder builder = SqlBuilder.update(p)
    .set(p.Name.eq("Tom"), p.CountryID.eq(100), p.CityID.eq(200))
    .where(p.PeopleID.eq(1));
 
SqlBuilder builder = SqlBuilder.insertInto(p, p.Name, p.CountryID)
    .values(p.Name.of("Jerry"), p.CountryID.of(dbShard));

通过BatchUpdateBuilder批量更新数据库int[] batchUpdate(BatchUpdateBuilder builder)方法

示例:

String[] statements = new String[]{
    "INSERT INTO " + TABLE_NAME + "( Name, CityID, ProvinceID, CountryID)"
    + " VALUES( 'test', 10, 1, 1)",
    "INSERT INTO " + TABLE_NAME + "( Name, CityID, ProvinceID, CountryID)"
    + " VALUES( 'test', 10, 1, 1)",
    "INSERT INTO " + TABLE_NAME + "( Name, CityID, ProvinceID, CountryID)"
    + " VALUES( 'test', 10, 1, 1)",};
BatchUpdateBuilder builder = new BatchUpdateBuilder(statements);
int[] ret = dao.batchUpdate(builder);

单条语句,多组值更新:

Person.PersonDefinition p = Person.PERSON;
 
BatchUpdateBuilder builder = new BatchUpdateBuilder(
      "INSERT INTO " + TABLE_NAME + "( Name, CityID, ProvinceID, CountryID) VALUES( ?, ?, ?, ?)",
      varcharVar("Name"), integerVar("CityID"), integerVar("ProvinceID"), integerVar("CountryID"));
builder.addBatch("test", 10, 1, 1);
...       
int[] ret = dao.batchUpdate(builder);
 
//另外一种方式,直接用列定义参数
BatchUpdateBuilder builder = new BatchUpdateBuilder(
      "INSERT INTO " + TABLE_NAME + "( Name, CityID, ProvinceID, CountryID) VALUES( ?, ?, ?, ?)",
      p.Name, p.CityID, p.ProvinceID, p.CountryID);
         
builder.addBatch("test", 10, 1, 1);

使用SqlBuilder

BatchUpdateBuilder builder = new BatchUpdateBuilder(
      SqlBuilder.insertInto(p, p.Name, p.CityID, p.ProvinceID, p.CountryID).values(
      varcharVar("Name"), integerVar("CityID"), integerVar("ProvinceID"), integerVar("CountryID")));
builder.addBatch("test", 10, 1, 1);
。。。
 
//另外一种定义参数的方式
BatchUpdateBuilder builder = new BatchUpdateBuilder(
      SqlBuilder.insertInto(p, p.Name, p.CityID, p.ProvinceID, p.CountryID).values(
      p.Name.var(), p.CityID.var(), p.ProvinceID.var(), p.CountryID.var()));
builder.addBatch("test", 10, 1, 1);
 
//Update的例子:
BatchUpdateBuilder builder = new BatchUpdateBuilder(
        SqlBuilder.update(p).set(
                p.Name.eq(varcharVar("Name")),
                p.CityID.eq(integerVar("CityID")),
                p.ProvinceID.eq(integerVar("ProvinceID")),
                p.CountryID.eq(integerVar("CountryID")))
                .where(p.DataChange_LastTime.eq(timestampVar("DataChange_LastTime"))));
builder.addBatch("test", 10, 1, 1, new java.sql.Timestamp(new Date().getTime()));
builder.addBatch("test", 10, 1, 1, new java.sql.Timestamp(new Date().getTime()));
builder.addBatch("test", 10, 1, 1, new java.sql.Timestamp(new Date().getTime()));
dao.batchUpdate(builder);

删除操作

按主键删除int deleteByPk(T pk, Hints...hints)方法

示例:

Person pk = new Person();
pk.setPeopleID(peopleId);
assertEquals(1, dao.deleteByPk(pk));

按样例删除int deleteBySample(T sample, Hints...hints)方法

示例:

Person sample = new Person();
sample.setName("test");
dao.deleteBySample(sample);

批处理方式删除多条记录int[] batchDelete(List<T> entities, Hints...hints)方法

示例:

List<Person> pl = new ArrayList<>();
for (int k = 0; k < 4; k++) {
    Person pk = new Person();
    pk.setPeopleID(peopleId);
    pk.setCountryID(k);
    pk.setCityID(k);
    pl.add(pk);
}
int[] ret = dao.batchDelete(pl);

存储过程调用操作

调用单个存储过程T call(CallBuilder builder)方法

示例: 包含input参数:

CallBuilder cb = new CallBuilder(SP_WITHOUT_OUT_PARAM);
cb.registerInput("v_id", JDBCType.INTEGER, 7);
cb.registerInput("v_cityID", JDBCType.INTEGER, 7);
cb.registerInput("v_countryID", JDBCType.INTEGER, 7);
cb.registerInput("v_name", JDBCType.VARCHAR, "666");
dao.call(cb);

包含output参数:

CallBuilder cb = new CallBuilder(SP_WITH_OUT_PARAM);
cb.registerInput("v_id", JDBCType.INTEGER, 4);
cb.registerOutput("count", JDBCType.INTEGER);
dao.call(cb);

包含inout参数:

CallBuilder cb = new CallBuilder(SP_WITH_IN_OUT_PARAM);
cb.registerInput("v_id", JDBCType.INTEGER, 3);
cb.registerInput("v_cityID", JDBCType.INTEGER, 7);
cb.registerInput("v_countryID", JDBCType.INTEGER, 7);
cb.registerInputOutput("v_name", JDBCType.VARCHAR, "666");
cb.hints().inShard(i);
dao.call(cb);

批量调用存储过程int[] batchCall(BatchCallBuilder builder)方法

示例: 包含input参数:

BatchCallBuilder cb = new BatchCallBuilder(SP_WITHOUT_OUT_PARAM);
cb.registerInput("v_id", JDBCType.INTEGER);
cb.registerInput("v_cityID", JDBCType.INTEGER);
cb.registerInput("v_countryID", JDBCType.INTEGER);
cb.registerInput("v_name", JDBCType.VARCHAR);
 
cb.addBatch(7, 7, 7, "777");
cb.addBatch(17, 17, 17, "1777");
cb.addBatch(27, 27, 27, "2777");
int[] ret = dao.batchCall(cb);

事务操作

无返回值的事务execute(Transaction transaction, Hints...hints)方法

示例: 包含input参数:

PersonDefinition p = Person.PERSON;
dao.execute(() -> {
    SqlBuilder builder = SqlBuilder.selectAllFrom(p)
        .where(p.PeopleID.gt(0))
        .orderBy(p.PeopleID.asc())
        .into(Person.class);
    List<Person> plist = dao.query(builder);
    assertEquals(4, plist.size());
    assertArrayEquals(new int[] {1, 1, 1, 1}, dao.batchDelete(plist));
    builder = SqlBuilder.selectAllFrom(p)
        .where(p.PeopleID.gt(0))
        .orderBy(p.PeopleID.asc())
        .into(Person.class);
    assertEquals(0, dao.query(builder).size());
             
    assertEquals(4, dao.insert(plist));
    builder = SqlBuilder.selectAllFrom(p)
        .where(p.PeopleID.gt(0))
        .orderBy(p.PeopleID.asc())
        .into(Person.class);
    assertEquals(4, dao.query(builder).size());
});

带返回值的事务T execute(CallableTransaction<T> transaction, Hints...hints)方法

示例: 包含input参数:

PersonDefinition p = Person.PERSON;
List<Person> plistx = dao.execute(() -> {
    SqlBuilder builder = SqlBuilder.selectAllFrom(p)
            .where(p.PeopleID.gt(0))
            .orderBy(p.PeopleID.asc())
            .into(Person.class);
    List<Person> plist = dao.query(builder);
    assertEquals(4, plist.size());
     
    assertArrayEquals(new int[] {1, 1, 1, 1}, dao.batchDelete(plist));
     
    builder = SqlBuilder.selectAllFrom(p)
             .where(p.PeopleID.gt(0))
             .orderBy(p.PeopleID.asc())
             .into(Person.class);
    assertEquals(0, dao.query(builder).size());
     
    assertEquals(4, dao.insert(plist));
    builder = SqlBuilder.selectAllFrom(p)
             .where(p.PeopleID.gt(0))
             .orderBy(p.PeopleID.asc())
             .into(Person.class);
    assertEquals(4, dao.query(builder).size());
    return plist;
});

支持注解声明式@DasTransactional(logicDbName = "logicDBName")事务方法

@DasTransactional需要在Spring容器环境下才能工作,此注解需修饰Spring bean内定义的方法。 示例:

@Service
public class MyService {
 
    @DasTransactional(logicDbName = "logicDBName")
    public void execute() {
        //DAS的操作
    }
...
}

同时支持单元测试情景下的自动回滚,例如:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
     DasTransactionalEnabler.class, //需要注入此bean
     AnnotationAwareAspectJAutoProxyCreator.class, //需要注入此bean,等价于XML中声明<aop:aspectj-autoproxy/>
     DasTest.class, 
     DaoBean.class})
@SpringBootTest
public class DasTest  {
    @Autowired
    DaoBean daoBean;

    @Test
    public void test() throws SQLException {
        daoBean.foo();
    }
}

@Component
public class DaoBean {

    @DasTransactional(logicDbName = DB_NAME, rollback = true) //foo方法中的数据操作都会被自动rollback
    public void foo() throws SQLException {
        //DAS的操作
    }
}
Clone this wiki locally