Skip to content

Java客户端 Hints说明

He, Jiehui edited this page Apr 2, 2018 · 37 revisions

简介

本页面包含Dal Hints的详细说明和用法。DalHints是在必须的DB操作参数之外额外传递给DAL的参数,用于:

  1. 提供DAL完成操作的额外指令,以覆盖缺省值或者缺省计算结果
  2. 传入特殊参数,例如排序器
  3. 传出特殊返回值,例如自增id

注意

DalHints会保留DAL处理的上下文或中间数据,为线程不安全对象。DalHints不应该作为静态或共享变量在不同调用或线程中共用。每次调用DAL的API的时候,请重新生成DalHints的实例。

使用方式

可用的Dal Hint定义在DalHintEnum,一般通过

hints.set(DalHintEnum.timeout, 1000);

的方式设置。但常用的hints一般会有对应的简化方法直接设置,例如

DalHints hints = context.getHints().timeout(1);

hints支持流式计算风格,可以在一行代码里面设置多个hints

hints.inAllShards().sequentialExecute().asyncExecution(),

关于hints的具体用法,可以参考功能简介

数据库连接相关hints

timeout

DB操作超时时间,单位秒

DalHints hints = context.getHints().timeout(1);

或者

hints.set(DalHintEnum.timeout, 1000);

masterOnly

指定操作必须使用master数据库。一般来说,CUD操作走master,但R走slave,如果希望R也走matsre,可以使用这个hint

hints.masterOnly()

或者

hints.set(DalHintEnum.masterOnly);

isolationLevel

指定操作的事务隔离级别,取值列表如下:

Connection.TRANSACTION_READ_UNCOMMITTED

Connection.TRANSACTION_READ_COMMITTED

Connection.TRANSACTION_REPEATABLE_READ

Connection.TRANSACTION_SERIALIZABLE

Connection.TRANSACTION_NONE

hints.setIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ);

designatedDatabase

Since 1.6.0

允许用户指定操作执行于当前逻辑数据库下对应的某个物理数据库上。

一般情况下,例如逻辑数据库L下面有3个物理数据库M1,M2,M3。数据库操作会随机选择其中某个执行,特殊情况下,如果用户希望在某个特定的数据库上执行,可以通过inDatabase方法设置designatedDatabase来实现。

new DalHints().inDatabase(M1)

结果集相关

resultSetType

指明查询结果集的类型。取值为ResultSet.TYPE_FORWARD_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, or ResultSet.TYPE_SCROLL_SENSITIVE之一

hints.set(DalHintEnum.resultSetType, ResultSet.TYPE_FORWARD_ONLY);

resultSetConcurrency

指定结果集的并发属性。取值为ResultSet.CONCUR_READ_ONLY or ResultSet.CONCUR_UPDATABLE之一

hints.set(DalHintEnum.resultSetConcurrency, ResultSet.CONCUR_UPDATABLE);

fetchSize

设置resultSet每次向数据库取的行数

hints.set(DalHintEnum.fetchSize, 1000);

skipResultsProcessing

针对调用存储过程的参数设置。如果设置了,则不处理存储过程内部产生的结果集和影响行数。这个参数不影响返回的out参数的获得。

hints.set(DalHintEnum.skipResultsProcessing);

maxRows

设置resultset最多只返回的行数

hints.set(DalHintEnum.maxRows, 3000);

partialQuery

Since dal-cleint 1.6.2

Columns that will be included for query。用于在查询操作中指定希望获得的部分字段。

设置以后,对于单表操作将只查询和映射给定的字段;对于自定义的sql,将按照原始的查询语句查询但只映射给定的字段

单表例子

test = client.queryFirst("1=1", new StatementParameters(), new DalHints().partialQuery("Name","CountryID").inShard(1).inTableShard(1));

自定义例子

return (FreeEntityPojo) queryDao.query(builder, parameters, hints.partialQuery("PeopleID", "Name", "CityID"));

allowPartial

Since dal-client 1.8.2

Allow columns in result set do not match columns declared in entity or partialQuery.

It will populate the common set of columns from result set and entity columns.

It request extractor or mapper to be HintsAwareExtractor or HintsAareMapper to do the required work

允许结果集中的列与实体或指定的列不一至。

当不一至的时候,DAL会取两者的交集来做映射。

allowPartial相当于同时具有partialQuery和ignorMIssingFields的功能。

该hints的使用需要获取查询结果的ResultsetMetaData,该操作有可能会导致一次额外的数据库网络访问,并导致额外的开销。因此请酌情试用。

需要支持HintsAwareExtractor 的extractor和HintsAareMapper 的mapper。

目前DalRowMapperExtractor和DalSingleResultExtractor,DalDefaultJpaParser与DalDefaultJpaMapper为缺省支持的实现类。

下面的例子说明了其用法。

findFreeFirstMismatch方法会查询Person表,表里面有6个字段:PeopleID, Name, CityID, ProvinceID, CountryID, DataChange_LastTime。

FreeEntityMismatchPojo包含6个字段:PeopleID, Name, CityID, Province, Country, Time。

两者交集为PeopleID, Name, CityID。

测试案例的含义是,如果DalHints没有任何设置,则会报告ResultMappingError的错误

反之设置了allowPartial则Dal会取两者交集做映射。

    hints = new DalHints().allowPartial();
    ret = findFreeFirstMismatch(name, cityIds, hints.inAllShards());

ignoreMissingFields

Since dal-cleint 1.6.2

If it is OK to allow some column not defined in pojo.

决定如果查询出来的列没有对应的实体属性是否报错

hints = new DalHints().ignoreMissingFields();
ret = findFreeFirstMissingFields(name, cityIds, hints.inShard(1));

操作相关

continueOnError

当对多个entity进行CUD操作的时候,决定是否在某个更新出错的时候继续对剩下的entity进行操作。但对批处理的无效。

hints.continueOnError()

hints = new DalHints(DalHintEnum.continueOnError);

int count = dao.insert(hints, pList);

updateNullField

单表操作时,指定是否把entity里面为null的字段也进行update。如果不设置缺省为忽略,即某个字段如果为null,则不会主动把该字段对应的数据库字段也置为null

hints.set(DalHintEnum.updateNullField);

enableIdentityInsert

缺省的insert会把传入的id去掉,如果该id对应的数据库字段是自增长的主键,使用这个hint允许插入的时候使用传入的id

dao.insert(new DalHints().enableIdentityInsert(), daoPojo);

setIdentityBack

since 1.15.0

将生成的主键设置回原来的pojo。注意在异步执行的情况下,主线程可能不能及时刷新到其他线程生成的主键。可以把主键对应的属性标记为volatile来避免

KeyHolder holder = new KeyHolder();
int[] res = dao.insert(new DalHints().setIdentityBack(), holder, entities);
int i = 0;
for(ClientTestModelJpa pojo: entities)
    Assert.assertEquals(holder.getKey(i++).intValue(), pojo.getId().intValue());

asyncExecution

在调用任何DAO的API之前设置DalHints为异步执行模式,则执行的结果会放到hints里面,原方法返回null或0。应用在调用结束后取得Future即可。 hints = new DalHints().asyncExecution();

(ClientTestModel)hints.getAsyncResult().get();

sequentialExecution

指定跨shard操作是否顺序执行,缺省情况下,如果操作需要在多个shard上执行,dal会采取并行执行的方式,通过这个hint可以指定为顺序执行。

hints.inAllShards().sequentialExecute().asyncExecution()

resultCallback

在调用任何DAO的API之前设置DalHints为异步回调模式, 传入回调接口,系统会根据执行情况的正常与否,调用不同的API。参数为DalResultCallback。

hints = new DalHints().callbackWith(callback);

DefaultResultCallback是DAL提供的缺省callback实现,可以参考或使用。

异步和跨shard执行可以结合使用,如下

hints.inAllShards().sequentialExecute().asyncExecution(),

sensitive

定义当前执行的SQL是否属于敏感语句。如果设置,在记录log的时候会把SQL替换为一个星号" * " . 如果需要设置参数的敏感性,需要调用StatementParameters.setSensitive()方法族,设置后,参数在log里面也会被替换为一个星号" * " 。注意敏感设置和加密是两个不同的概念,可以即敏感又加密。

hints.set(DalHintEnum.sensitive);

retrieveAllSpResults

Since 1.6.0

通过指定retrieveAllSpResults hint可以不用自定义获取参数就可以获得所有在SP执行过程中产生的查询与更新的中间结果。如果SqlServer的SET NO COUNT会导致无法返回更新值

//auto get all result parameters store procedure
private static final String CREATE_MULTIPLE_RESULT_SP_SQL = "CREATE PROCEDURE MULTIPLE_RESULT_SP_SQL("
        + "dal_id int,"
        + "quantity int,"
        + "type smallint,"
        + "INOUT address VARCHAR(64))"
        + "BEGIN UPDATE dal_client_test "
        + "SET quantity = quantity, type=type, address=address "
        + "WHERE id=dal_id;"
        + "SELECT ROW_COUNT() AS result;"
        + "SELECT 1 AS result2;"
        + "UPDATE dal_client_test "
        + "SET `quantity` = quantity + 1, `type`=type + 1, `address`='aaa';"
        + "SELECT 'abc' AS result3, 456 AS count2;"
        + "SELECT * from dal_client_test;"
        + "SELECT 'output' INTO address;"
        + "END";
/**
 * Test the call function with retrieveAllResultsFromSp parameters
 * @throws SQLException
 */
@Test
public void callTestWithAutoParameters() throws SQLException{
    String callSql = "call " + MULTIPLE_RESULT_SP_SQL + "(?,?,?,?)";
    StatementParameters parameters = new StatementParameters();
    parameters.set("dal_id", Types.INTEGER, 1);
    parameters.set("quantity", Types.INTEGER, 10);
    parameters.set("type", Types.SMALLINT, 3);
    //parameters.set("address", Types.VARCHAR, "SZ INFO");
    parameters.registerInOut("address", Types.VARCHAR, "SZ INFO");
     
    DalHints hints = new DalHints().retrieveAllResultsFromSp();
    Map<String, ?> res = client.call(callSql, parameters, hints);
    System.out.println(res);
    Assert.assertTrue(null != res);
    Assert.assertEquals(6, res.size());
    Assert.assertTrue(res.containsKey("address"));
    Assert.assertEquals("output", res.get("address"));
    Assert.assertEquals("output", parameters.get("address", ParameterDirection.InputOutput).getValue());
     
    List<ClientTestModel> models = this.queryModelsByIds(1);
    Assert.assertEquals(1, models.size());
    Assert.assertEquals("aaa", models.get(0).getAddress());
}

excludedColumns

Since dal-cleint 1.6.2

Columns that will be excluded for update

针对标准DAO单表更新操作,给定的字段将不会被更新。支持batchUpdate和update。可以和includedColumns配合使用。excludedColumns的优先级更高

int[] result = dao.batchUpdate(new DalHints().exclude("dbIndex"), pojos);

includedColumns

Since dal-cleint 1.6.2

Columns that will be included for update

针对标准DAO单表更新操作,给定的字段被更新,没出现的字段将被忽略。支持batchUpdate和update。

int[] result = dao.batchUpdate(new DalHints().include("address", "quantity"), pojos);

可以和excludedColumns配合使用。excludedColumns的优先级更高

DalHints hints = new DalHints().exclude("dbIndex").include("quantity", "dbIndex", "address");
int[] result = dao.batchUpdate(hints, pojos);

selectByNames

since 1.13.8

缺省情况下,通过DalTableDao查询会使用SELECT* 来声明字段列表,DBA反馈这样做会消耗数据库性能。在设置了selectByNames的情况下,DalTableDao将按照明确的字段名查询。

数据库或表的shard相关

shard

通过直接指定shard id的方式决定数据库操作针对哪个shard

model = dao.queryByPk(1, new DalHints().inShard(String.valueOf(i)));

tableShard

通过直接指定table shard id的方式决定数据库操作针对哪个table shard。可以直接调用DalHints().inTableShard(i)

model = dao.queryByPk(pk, new DalHints().inTableShard(i));

shardColValues

用于分库分表判断的 Map<String, Integer>。key为column name;value 为具体的值。当Dal.config里面配置的Sharding strategy是ShardColModShardStrategy时起作用。

model = dao.queryByPk(1, new DalHints().setShardColValue("index", i));

shardValue

帮助定位DB shard的值

model = dao.queryByPk(1, new DalHints().setShardValue(String.valueOf(i)));

model = dao.queryByPk(1, new DalHints().setShardValue(i));

tableShardValue

帮助定位table shard的值

model = dao.queryByPk(pk, new DalHints().setTableShardValue(i));

shardBy

指定用于shard判断的参数名称

List<Integer> inParam = new ArrayList<>();

inParam.add(0);

inParam.add(1);

parameters.setInParameter(1, "type", Types.INTEGER, inParam);

new DalQueryDao(DATABASE_NAME).query(

builder, parameters,

hints.shardBy("type"));

shards

如果希望操作在给定的shard范围上执行,可以通过Dalhints的如下设置来实现 Set<String> shards = new HashSet<>(); shards.add("0"); shards.add("1"); hints.inShards(shards);

allShards

如果希望操作在所有shard上都执行,可以通过Dalhints的如下设置来实现

hints.inAllShards();

resultMerger

由于查询可以在多个shard上执行,为了保证返回的结果是用户希望的顺序,DAL支持用户自定义的结果合并接口ResultMerger。

List<Short> result = queryListInAllShard(hints.mergeBy(new TestResultMerger()));

resultSorter

由于查询可以在多个shard上执行,为了保证返回的结果是用户希望的顺序,可以简单的传入一个Comparator作为排序的sorter。这个sorter会配合缺省的ResultMerger实现。

List<Short> result = queryListInAllShard(hints.sortBy(new TestComparator()));

其他

forceAutoCommit

Note. This hint is actually useless, there is no use case that we need to use it.

used in batch sp batch update, when set, the connection auto commit will be true.

Note that SQL server does not support auto commit for batch update.

My sql support it. But this won't work when mysql has the following settings in datasource.xml: connectionProperties="rewriteBatchedStatements=true;allowMultiQueries=true"

Clone this wiki locally