-
Notifications
You must be signed in to change notification settings - Fork 448
Java客户端 功能简介
dal客户端也称dal client或client。是实现数据库访问的api层。主要是配合code gen生成的代码,提供生成代码使用的底层api。请直接使用code gen产生的代码,不要自己修改或试图手工调用api。
本文主要介绍一些常用的功能如何通过dal client完成
如果数据库支持自增长id,那么在插入包含自增长id的表的时候,DAL会过滤掉原有的id,并在KeyHolder里面返回生产的id。
支持的DalTableDao API,参数里面包含KeyHolder的插入操作,都支持
public int combinedInsert(DalHints hints, KeyHolder keyHolder, List<T> daoPojos)
public int[] insert(DalHints hints, KeyHolder keyHolder, List<T> daoPojos)
通过code gen生成的DAO里面的insert方法如果包含KeyHolder参数,说明该方法支持自增长id,并且用法一样。
不是所有的插入操作都会返回自增长id。批处理的插入就无法返回。
public int[] batchInsert(DalHints hints, List<T> daoPojos)
如果只插入一条数据,可以通过getKey来得到返回的自增长id public Number getKey()
如果是一系列数据库,可以通过getIdList()得到 public List getIdList()
其他API
得到第n个pojo对应的id
public Number getKey(int index)
得到数据库返回的原始格式的key,仅限于插入一条的情况
public Map<String, Object> getKeys()
得到数据库返回的原始格式的key,适用于插入多条的情况
public List<Map<String, Object>> getKeyList()
KeyHolder里面放的返回的自增长id的的顺序是传入到insert或combinedInsert里面的pojo的顺序。即使在逻辑数据库支持分片的情况下,DAL也会按照pojo的顺序汇总各个shard生成id的结果
KeyHolder支持DAO层面的异步操作,如果异步调用DAO,对KeyHolder的访问不受影响,用法和同步一致
代码示例
调用生成的DAO,插入一条
private void insert(HttpServletRequest request, HttpServletResponse response) throws Exception {
KeyHolder keyHolder = new KeyHolder();
dao.insert(readHints(request).setKeyHolder(keyHolder), readPeople(request));
response.getWriter().write("Insert success. Id : " + keyHolder.getKey());
}
代码示例2,插入多条
holder = createKeyHolder();
res = dao.combinedInsert(new DalHints().inShard(i), holder, entities);
assertResEquals(3, res);
assertEquals(3 + j++ * 3, getCountByDb(dao, i));
assertKeyHolder(holder);
如果表的主键是自增长的字段,缺省的插入操作会把传入的主键去掉。可以通过设置hint来允许使用传入的主键
dao.insert(new DalHints().enableIdentityInsert(), daoPojo);
例子
for(int i = 1; i < 11; i++) {
PersonGen daoPojo = createPojo(i);
try {
dao.insert(new DalHints().enableIdentityInsert(), daoPojo);
} catch (SQLException e) {
e.printStackTrace();
}
}
在code gen里面创建构建和自定义DAO的时候,可以指定参数是否为敏感。
如果参数是敏感的,则用特殊的setSenstive方法来设置参数。否则用正常的set。
public int test_build_insert (String Name, Integer Age, DalHints hints) throws SQLException {
String sql = SQLParser.parse("INSERT INTO person (`Name`,`Age`) VALUES ( ? , ? )");
StatementParameters parameters = new StatementParameters();
hints = DalHints.createIfAbsent(hints);
int i = 1;
parameters.setSensitive(i++, "Name", Types.VARCHAR, Name);
parameters.setSensitive(i++, "Age", Types.INTEGER, Age);
return client.update(sql, parameters, hints);
}
在entity里面可以通过@Sensitive来指定字段是否为敏感。
@Id
@Column(name="ID")
@GeneratedValue(strategy = GenerationType.AUTO)
@Type(value=Types.INTEGER)
@Sensitive(value=true)
private Integer iD;
@Column(name="Name")
@Type(value=Types.VARCHAR)
@Sensitive(value=true)
private String name;
@Column(name="Age")
@Type(value=Types.INTEGER)
@Sensitive(value=true)
private Integer age;
Senstive会影响log记录的信息。如果参数或field被标记为senstive,则log的时候会把对应的值替换为*
允许用户通过一个DB请求执行多条sql。相关API位于DalQueryDao public List<?> query(MultipleSqlBuilder mqr, StatementParameters parameters, DalHints hints) throws SQLException;
MultipleSqlBuilder提供多个重载方法方便构建MultipleSqlBuilder
public <T> MultipleSqlBuilder add(String sql, DalResultSetExtractor<T> extractor, ResultMerger<T> merger);
public <T> MultipleSqlBuilder addQuery(String sql, DalRowMapper<T> mapper);
public <T> MultipleSqlBuilder addQuery(String sql, DalRowMapper<T> mapper, ResultMerger<List<T>> merger);
public <T> MultipleSqlBuilder addQuery(String sql, DalRowMapper<T> mapper, Comparator<T> sorter);
public <T> MultipleSqlBuilder addQuery(String sql, Class<T> clazz) throws SQLException;
public <T> MultipleSqlBuilder addQuery(String sql, Class<T> clazz, ResultMerger<List<T>> merger) throws SQLException;
public <T> MultipleSqlBuilder addQuery(String sql, Class<T> clazz, Comparator<T> sorter) throws SQLException;
public MultipleSqlBuilder addQuery(String sql, DalRowCallback callback);
代码示例
private String sqlList = "select * from " + TABLE_NAME;
private String sqlListQuantity = "select quantity from " + TABLE_NAME;
private String sqlObject = "select * from " + TABLE_NAME + " where id = ? and type=0";
private String sqlFirst = "select * from " + TABLE_NAME + " where id = ?";
private String sqlNoResult = "select * from " + TABLE_NAME + " where id = -1";
private String sqlIdInParam = "select * from " + TABLE_NAME + " where id in (?)";
private List queryMultipleAllShards(DalHints hints) throws SQLException {
DalQueryDao dao = new DalQueryDao(DATABASE_NAME);
StatementParameters parameters = new StatementParameters();
parameters.set(1, Types.INTEGER, 1);
MultipleSqlBuilder builder = new MultipleSqlBuilder();
// TODO add all add method
builder.addQuery(sqlList, new StatementParameters(), new ClientTestDalRowMapper());//mapper
builder.addQuery(sqlList, new StatementParameters(), new ClientTestDalRowMapper(), new DalListMerger<ClientTestModel>());//merger
builder.addQuery(sqlList, new StatementParameters(), ClientTestModel.class, new ClientTestModelComparator());//sorter
builder.addQuery(sqlListQuantity, new StatementParameters(), Integer.class, new DalListMerger<Integer>());//merger
builder.addQuery(sqlObject, parameters, Integer.class, new InteregrComparator());//soter
builder.addQuery(sqlNoResult, new StatementParameters(), new TestDalRowCallback3());//callback
List<Integer> inParam = new ArrayList<>();
inParam.add(0);
inParam.add(1);
inParam.add(2);
inParam.add(3);
inParam.add(4);
parameters.setInParameter(1, "type", Types.INTEGER, inParam);
builder.addQuery(sqlIdInParam, new StatementParameters(), ClientTestModel.class);
return dao.query(builder, hints.inAllShards());
}
目前DalTableDao和DalQueryDao里面的所有API都已支持异步操作。对异步的支持是通过DalHints来实现的,没有新添加API。对异步的支持是由内部的DalRequestExecutor来实现,在DAO这层发生,不涉及到DalClient的改动。
目前对异步的支持有两种形式:异步返回执行结果和异步调用回调接口。无论是那种形式都判定为异步操作。
在调用任何DAO的API之前设置DalHints为异步执行模式,则执行的结果会放到hints里面,原方法返回null或0。应用在调用结束后取得Future即可。
DalHints().asyncExecution()
代码示例如下
@Test
public void testQueryByPkAsync() throws SQLException {
ClientTestModel model = null;
DalHints hints;
for(int i = 0; i < mod; i++) {
// By shard
hints = new DalHints().asyncExecution();
if(i%2 == 0)
model = dao.queryByPk(1, hints.inShard(String.valueOf(i)));
else
model = dao.queryByPk(1, hints.inShard(i));
assertNull(model);
model = getModel(hints);
assertEquals(1, model.getId().intValue());
assertEquals(i, model.getTableIndex().intValue());
===
private ClientTestModel getModel(DalHints hints) throws SQLException {
try {
return (ClientTestModel)hints.getAsyncResult().get();
} catch (Exception e) {
throw new SQLException(e);
}
}
在调用任何DAO的API之前设置DalHints为异步回调模式, 传入回调接口,系统会根据执行情况的正常与否,调用不同的API。 注意
就算是使用回调形式,Dal还是会把异步执行的结果放到hints里面。以备查询的方便。应用如果用不到,可以简单忽略,无需处理。
代码示例如下
public interface DalResultCallback {
<T> void onResult(T result);
void onError(Throwable e);
}
@Test
public void testQueryByPkCallback() throws SQLException {
ClientTestModel model = null;
DalHints hints;
TestQueryResultCallback callback;
for(int i = 0; i < mod; i++) {
// By shard
callback = new TestQueryResultCallback();
hints = new DalHints().callbackWith(callback);
if(i%2 == 0)
model = dao.queryByPk(1, hints.inShard(String.valueOf(i)));
else
model = dao.queryByPk(1, hints.inShard(i));
assertNull(model);
model = callback.get();
assertEquals(1, model.getId().intValue());
assertEquals(i, model.getTableIndex().intValue());
====
private class TestQueryResultCallback implements DalResultCallback {
private AtomicReference<ClientTestModel> model = new AtomicReference<>();
@Override
public <T> void onResult(T result) {
model.set((ClientTestModel)result);
}
public ClientTestModel get() {
while(model.get() == null)
try {
Thread.sleep(1);
} catch (Exception e) {
return null;
}
return model.get();
}
@Override
public void onError(Throwable e) {
// TODO Auto-generated method stub
}
}
为方便测试,Dal提供了回调接口的缺省实现DefaultResultCallback,可在com.ctrip.platform.dal.dao.helper找到。API如下
com.ctrip.platform.dal.dao.helper.DefaultResultCallback
onResult(T)
onError(Throwable)
waitForDone()//等待调用结束
waitForDone(int)//等待调用结束,可以设置timeout,单位为毫秒
isDone()//是否结束
isSuccess()//是否成功
getResult()//得到成功的结果
getError()//得到出错的异常
reset()//重置,可以再次利用