本文主要整理JAVA開發(fā)中的一些模板代碼,作為平時(shí)參考使用浙芙。
1 MongoTemplate
版本:mongoDb4.0
1.1 寫入數(shù)據(jù)
JSONObject json = new JSONObject();
json.put("userName", "張三");
json.put("age", 20);
json.put("sex", "男");
json.put("oneFlag", true);
json.put("towFlag", true);
json.put("indexNum", i + 1);
json.put("delFlag", "0");
mongoTemplate.insert(json, "t_user_info");
1.2 更新數(shù)據(jù)
// ----------[組裝查詢條件]----------
Query query = new Query();
query.addCriteria(Criteria.where("indexNum").is(1));
// ----------[組裝更新邏輯]----------
Update update = new Update();
update.set("userName", "李四");
update.set("age", 19);
// ----------[更新數(shù)據(jù)]----------
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, "t_user_info");
System.out.println("匹配數(shù)量:"+updateResult.getMatchedCount());
// 如果更新后數(shù)據(jù)和更新前一樣损敷,則getModifiedCount()返回0
System.out.println("更新數(shù)量:"+updateResult.getModifiedCount());
1.3 基本查詢
Query query = new Query();
// ----------[拼接查詢條件]----------
// 等于
query.addCriteria(Criteria.where("delFlag").is("0"));
// 字符串模糊查詢
query.addCriteria(Criteria.where("userName").regex("^.*張三.*$"));
// 數(shù)值比較(大于)
query.addCriteria(Criteria.where("age").gte(20));
// 集合查詢
query.addCriteria(Criteria.where("sex").in(Arrays.asList("男", "女")));
// 或條件
query.addCriteria(new Criteria().orOperator(Criteria.where("oneFlag").is(true), Criteria.where("towFlag").is(true)));
// ----------[排序]----------
query.with(Sort.by(new Sort.Order(Sort.Direction.DESC, "age"), new Sort.Order(Sort.Direction.ASC, "indexNum")));
// ----------[指定查詢字段]----------
query.fields().include("userName").include("age").include("sex").include("indexNum");
// ----------[分頁]----------
// 當(dāng)前頁數(shù)
int pageNumber = 1;
// 每頁數(shù)量
int pageLimit = 10;
query.skip((pageNumber - 1) * pageLimit).limit(pageLimit);
// ----------[執(zhí)行查詢]----------
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
// 輸出內(nèi)容:【Query: { "delFlag" : "0", "userName" : { "$regex" : "^.*張三.*$", "$options" : ""}, "age" : { "$gte" : 20}, "sex" : { "$in" : ["男", "女"]}, "$or" : [{ "oneFlag" : true}, { "towFlag" : true}]}, Fields: { "indexNum" : 1, "sex" : 1, "userName" : 1, "age" : 1}, Sort: { "age" : -1, "indexNum" : 1}】
System.out.println(query.toString());
1.4 組裝查詢條件
(1)錯(cuò)誤場(chǎng)景
mongoTemplate的query在組裝查詢條件時(shí)男应,執(zhí)行query.addCriteria
搞疗,不能對(duì)統(tǒng)一字段執(zhí)行多次此操作,否則會(huì)拋出InvalidMongoDbApiUsageException
異常剥懒。
錯(cuò)誤代碼,如下:
Query query = new Query();
// 對(duì)字段age合敦,添加多個(gè)條件
query.addCriteria(Criteria.where("age").gte(20));
query.addCriteria(Criteria.where("age").lte(30));
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
此種情況初橘,拋出異常信息如下:
org.springframework.data.mongodb.InvalidMongoDbApiUsageException: Due to limitations of the com.mongodb.BasicDocument, you can't add a second 'age' criteria. Query already contains '{ "age" : { "$gte" : 20}}'
(2)解決方案-1
同一個(gè)字段,拼接多個(gè)條件充岛。
Query query = new Query();
query.addCriteria(Criteria.where("age").gte(20).lte(30));
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
// 輸出內(nèi)容:【Query: { "age" : { "$gte" : 20, "$lte" : 30}}, Fields: {}, Sort: {}】
System.out.println(query);
(3)解決方案-2(建議)
將查詢條件保檐,拼接為and模式
List<Criteria> criteriaList = new ArrayList<>();
criteriaList.add(Criteria.where("age").gte(20));
criteriaList.add(Criteria.where("age").lte(30));
Criteria[] criteriaArr= new Criteria[criteriaList.size()];
criteriaList.toArray(criteriaArr);
Criteria criteria = new Criteria();
criteria.andOperator(criteriaArr);
Query query = new Query();
query.addCriteria(criteria);
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
// 輸出內(nèi)容:【Query: { "$and" : [{ "age" : { "$gte" : 20}}, { "age" : { "$lte" : 30}}]}, Fields: {}, Sort: {}】
System.out.println(query);
1.5 游標(biāo)查詢
試用于海量數(shù)據(jù)查詢
// ----------[組裝查詢條件]----------
Query query = new Query();
query.addCriteria(Criteria.where("sex").is("男"));
// ----------[游標(biāo)查詢]----------
try(MongoCursor<Document> cursor = mongoTemplate.getCollection("t_user_info").find(query.getQueryObject()).batchSize(1000).noCursorTimeout(true).cursor();) {
while (cursor.hasNext()){
Document document = cursor.next();
// TODO 執(zhí)行業(yè)務(wù)方法
}
}catch (Exception e){
e.printStackTrace();
}
1.6 聚合查詢
// ----------[分頁相關(guān)]----------
// 頁數(shù)
long pageNumber = 1;
// 每頁數(shù)量
long pageLimit = 10;
// ----------[查詢條件相關(guān)]----------
// where
Criteria whereCriteria = new Criteria().andOperator(Criteria.where("delFlag").is("0"));
// having
Criteria havingCriteria = new Criteria().andOperator(Criteria.where("count").gte(500));
// ----------[分組查詢]----------
Aggregation aggregation = Aggregation.newAggregation(
// 條件(match在group前作為where使用)
Aggregation.match(whereCriteria),
// 分組條件(group by sex, userName)
Aggregation.group("sex", "userName").max("indexNum").as("maxIndexNum").count().as("count"),
//having(match在group后作為having使用)
Aggregation.match(havingCriteria),
// 映射字段到前臺(tái)
Aggregation.project("sex", "userName", "maxIndexNum", "count"),
// 排序
Aggregation.sort(Sort.by(new Sort.Order(Sort.Direction.ASC, "sex"))),
// 分頁
Aggregation.skip((pageNumber - 1) * pageLimit),
Aggregation.limit(pageLimit)
);
// Mongodb規(guī)定了aggregate管道聚合的返回?cái)?shù)據(jù)不能超過`16M`,超過16M就會(huì)報(bào)異常錯(cuò)誤崔梗。解決方法就是設(shè)置`allowDiskUse:true`夜只,即允`許使用磁盤緩存`
aggregation = aggregation.withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());
// 輸出內(nèi)容:【{ "aggregate" : "__collection__", "pipeline" : [{ "$match" : { "$and" : [{ "delFlag" : "0"}]}}, { "$group" : { "_id" : { "sex" : "$sex", "userName" : "$userName"}, "maxIndexNum" : { "$max" : "$indexNum"}, "count" : { "$sum" : 1}}}, { "$match" : { "$and" : [{ "count" : { "$gte" : 500}}]}}, { "$project" : { "sex" : "$_id.sex", "userName" : "$_id.userName", "maxIndexNum" : 1, "count" : 1}}, { "$sort" : { "sex" : 1}}, { "$skip" : { "$numberLong" : "0"}}, { "$limit" : { "$numberLong" : "10"}}], "allowDiskUse" : true}】
System.out.println(aggregation.toString());
List<Map> list = mongoTemplate.aggregate(aggregation, "t_user_info", Map.class).getMappedResults();
1.7 MapReduce
1.7.1 注意事項(xiàng)
Map注意事項(xiàng):
- 在map函數(shù)中,如果要引用當(dāng)前文檔自身蒜魄,可以使用
this
- 在一個(gè)map函數(shù)中可以任意
多次調(diào)用emit函數(shù)
來輸出具有key/value形式的中間數(shù)據(jù)
Reduce注意事項(xiàng):
- 如果
一個(gè)key只有一個(gè)value
扔亥,那么MongoDB不調(diào)用
reduce函數(shù) - 對(duì)于
同一個(gè)key
,reduce函數(shù)可能會(huì)執(zhí)行多次
1.7.2 mongo腳本
(1)輸出結(jié)果到實(shí)際的表
輸出結(jié)果到表中(以替換整張表的方式)
// 自定義map函數(shù)
var myMap = function() {
emit(this.sex, this.age);
}
// 自定義reduce函數(shù)
var myReduce = function(key, values) {
var totalAge = 0;
for (var i = 0; i < values.length; i++) {
totalAge = totalAge + values[i];
}
return totalAge;
}
// 執(zhí)行mapReduce方法
db.t_user_info.mapReduce(
// 自定義map函數(shù)
myMap,
// 自定義reduce函數(shù)
myReduce,
{
// 查詢條件
query: {"delFlag" : "0"},
// 輸出方式(整個(gè)替換輸出到表)(輸出到內(nèi)容中谈为,可用:"out: { inline: 1 }")
out: "map_reduce_result"
}
)
執(zhí)行后旅挤,輸出內(nèi)容如下:
/* 1 */
{
// 存儲(chǔ)數(shù)據(jù)的集合
"result" : "map_reduce_result",
// 執(zhí)行花費(fèi)的時(shí)間(毫秒)
"timeMillis" : 138.0,
"counts" : {
// 滿足條件被發(fā)送到map函數(shù)的文檔個(gè)數(shù)
"input" : 1500,
// 在map函數(shù)中emit被調(diào)用的次數(shù),也就是所有集合中的數(shù)據(jù)總量
"emit" : 1500,
// 滿足條件被發(fā)送到reduce函數(shù)的文檔個(gè)數(shù)
"reduce" : 15,
// 結(jié)果集合中的文檔個(gè)數(shù)
"output" : 2
},
// 是否成功伞鲫,成功為1
"ok" : 1.0,
"_o" : {
"result" : "map_reduce_result",
"timeMillis" : 138,
"counts" : {
"input" : 1500,
"emit" : 1500,
"reduce" : 15,
"output" : 2
},
"ok" : 1.0
},
"_keys" : [
"result",
"timeMillis",
"counts",
"ok"
],
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.0.11:40001",
"defaultDB" : "aaDb",
"authStatus" : {
"authRequired" : true,
"isMaster" : true,
"replSetGetStatus" : true
},
"_readMode" : "commands"
},
"_name" : "aaDb"
},
"_coll" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.0.11:40001",
"defaultDB" : "aaDb",
"authStatus" : {
"authRequired" : true,
"isMaster" : true,
"replSetGetStatus" : true
},
"_readMode" : "commands"
},
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.0.11:40001",
"defaultDB" : "aaDb",
"authStatus" : {
"authRequired" : true,
"isMaster" : true,
"replSetGetStatus" : true
},
"_readMode" : "commands"
},
"_name" : "aaDb"
},
"_shortName" : "map_reduce_result",
"_fullName" : "aaDb.map_reduce_result"
}
}
執(zhí)行結(jié)束后粘茄,表map_reduce_result中結(jié)果如下:
/* 1 */
{
"_id" : "女",
"value" : 12500.0
}
/* 2 */
{
"_id" : "男",
"value" : 19999.0
}
(2)輸出到內(nèi)存
如只想獲取,計(jì)算結(jié)果秕脓,不想將數(shù)據(jù)結(jié)果輸出到實(shí)際的表中柒瓣。可以將out進(jìn)行如下配置吠架,將結(jié)果芙贫,輸出到內(nèi)存中
:
out: { inline: 1 }
這個(gè)選項(xiàng)只有在結(jié)果集單個(gè)文檔大小在16MB限制范圍內(nèi)時(shí)才有效
完整腳本如下:
// 自定義map函數(shù)
var myMap = function() {
emit(this.sex, this.age);
}
// 自定義reduce函數(shù)
var myReduce = function(key, values) {
var totalAge = 0;
for (var i = 0; i < values.length; i++) {
totalAge = totalAge + values[i];
}
return totalAge;
}
// 執(zhí)行mapReduce方法
db.t_user_info.mapReduce(
// 自定義map函數(shù)
myMap,
// 自定義reduce函數(shù)
myReduce,
{
// 查詢條件
query: {"delFlag" : "0"},
// 輸出方式(內(nèi)存臨時(shí)表)
out: { inline: 1 }
}
)
輸出結(jié)果如下(可見results
屬性中,可以看到計(jì)算的最終結(jié)果)
/* 1 */
{
"results" : [
{
"_id" : "女",
"value" : 12500.0
},
{
"_id" : "男",
"value" : 19999.0
}
],
"timeMillis" : 81.0,
"counts" : {
"input" : 1500,
"emit" : 1500,
"reduce" : 15,
"output" : 2
},
"ok" : 1.0,
"_o" : {
"results" : [
{
"_id" : "女",
"value" : 12500.0
},
{
"_id" : "男",
"value" : 19999.0
}
],
"timeMillis" : 81,
"counts" : {
"input" : 1500,
"emit" : 1500,
"reduce" : 15,
"output" : 2
},
"ok" : 1.0
},
"_keys" : [
"results",
"timeMillis",
"counts",
"ok"
],
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.0.11:40001",
"defaultDB" : "aaDb",
"authStatus" : {
"authRequired" : true,
"isMaster" : true,
"replSetGetStatus" : true
},
"_readMode" : "commands"
},
"_name" : "aaDb"
}
}
1.7.3 mongoTemplate
(1)寫入到內(nèi)存中
// (1)定義map函數(shù)
String mapFun = "function() {\n" +
" emit(this.sex, this.age);\n" +
"}";
// (2)定義reduce函數(shù)
String reduceFun = "function(key, values) {\n" +
" var totalAge = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" totalAge = totalAge + values[i];\n" +
" }\n" +
" return totalAge;\n" +
"}";
// (3)執(zhí)行mapReduce方法
MapReduceResults<Document> mapReduceResults = mongoTemplate.mapReduce(new Query(Criteria.where("delFlag").is("0")), "t_user_info", mapFun, reduceFun, Document.class);
// (4)輸出執(zhí)行結(jié)果
Iterator<Document> iterator = mapReduceResults.iterator();
while (iterator.hasNext()) {
// Document{{_id=女, value=12500.0}}
// Document{{_id=男, value=19999.0}}
System.out.println(iterator.next());
}
(2)寫入到表
// (1)定義map函數(shù)
String mapFun = "function() {\n" +
" emit(this.sex, this.age);\n" +
"}";
// (2)定義reduce函數(shù)
String reduceFun = "function(key, values) {\n" +
" var totalAge = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" totalAge = totalAge + values[i];\n" +
" }\n" +
" return totalAge;\n" +
"}";
// (3)定義相關(guān)屬性配置
MapReduceOptions mapReduceOptions = MapReduceOptions.options().outputCollection("my_map_reduce_res");
// (4)執(zhí)行mapReduce方法
MapReduceResults<Document> mapReduceResults = mongoTemplate.mapReduce(new Query(Criteria.where("delFlag").is("0")), "t_user_info", mapFun, reduceFun, mapReduceOptions, Document.class);
// (5)輸出執(zhí)行結(jié)果
Iterator<Document> iterator = mapReduceResults.iterator();
while (iterator.hasNext()) {
// Document{{_id=女, value=12500.0}}
// Document{{_id=男, value=19999.0}}
System.out.println(iterator.next());
}
根據(jù)輸出結(jié)果诵肛,可見屹培,仍然可在對(duì)象MapReduceResults中獲取執(zhí)行結(jié)果默穴,執(zhí)行結(jié)束后,查詢數(shù)據(jù)庫褪秀,可見目標(biāo)表my_map_reduce_res中蓄诽,已生成我們需要的數(shù)據(jù)。
/* 1 */
{
"_id" : "女",
"value" : 12500.0
}
/* 2 */
{
"_id" : "男",
"value" : 19999.0
}
1.8 bulk
主要適用于批量更新
媒吗,減少更新請(qǐng)求的調(diào)用次數(shù)仑氛。也可混合使用(寫入、更新闸英、刪除)锯岖。
(1)寫法1
// ----------[定義更新集合]----------
List<Pair<Query, Update>> updatePairList = new ArrayList<>();
// ----------[組裝更新集合]----------
// 組裝更新數(shù)據(jù)1
Query query1 = new Query();
query1.addCriteria(Criteria.where("indexNum").is(1));
Update update1 = new Update();
update1.set("delFlag", "11");
Pair<Query, Update> updatePair1 = Pair.of(query1, update1);
updatePairList.add(updatePair1);
// 組裝更新數(shù)據(jù)2
Query query2 = new Query();
query2.addCriteria(Criteria.where("indexNum").is(2));
Update update2 = new Update();
update2.set("delFlag", "12");
Pair<Query, Update> updatePair2 = Pair.of(query2, update2);
updatePairList.add(updatePair2);
// ......
// ----------[更新數(shù)據(jù)]----------
BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "t_user_info");
operations.updateOne(updatePairList);
BulkWriteResult writeResult = operations.execute();
boolean result = writeResult.wasAcknowledged();
System.out.println("執(zhí)行結(jié)果:" + result);
(2)寫法2
// ----------[定義操作對(duì)象]----------
BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "t_user_info");
// ----------[組裝更新集合]----------
// 組裝更新數(shù)據(jù)1
Query query1 = new Query();
query1.addCriteria(Criteria.where("indexNum").is(1));
Update update1 = new Update();
update1.set("delFlag", "21");
operations.updateOne(query1,update1);
// 組裝更新數(shù)據(jù)2
Query query2 = new Query();
query2.addCriteria(Criteria.where("indexNum").is(2));
Update update2 = new Update();
update2.set("delFlag", "22");
operations.updateOne(query2,update2);
// ......
// operations.insert()
// operations.remove()
// ----------[更新數(shù)據(jù)]----------
BulkWriteResult writeResult = operations.execute();
boolean result = writeResult.wasAcknowledged();
System.out.println("執(zhí)行結(jié)果:" + result);
1.9 切換到主庫查詢
默認(rèn)
readPreference為ReadPreference.secondaryPreferred()
時(shí),可通過如下方式甫何,臨時(shí)設(shè)置從主庫讀取數(shù)據(jù)
出吹。
也可設(shè)置專門從主庫讀取數(shù)據(jù)的mongoTemplate句柄
。
// mongoTemplate切換到主庫查詢
try {
mongoTemplate.setReadPreference(ReadPreference.primary());
// TODO 執(zhí)行查詢操作......
}catch (Exception e){
// 異常(處理或拋出)
e.printStackTrace();
}finally {
// 切換回默認(rèn)的ReadPreference
mongoTemplate.setReadPreference(ReadPreference.secondary());
}
2 Mybatis
應(yīng)用數(shù)據(jù)庫:mysql
辙喂。測(cè)試使用建表腳本如下:
CREATE TABLE `c_m_phone` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`phone_first` varchar(20) DEFAULT NULL COMMENT '前綴號(hào)段',
`phone_provence` varchar(20) DEFAULT NULL COMMENT '手機(jī)所在省份',
`phone_city` varchar(20) DEFAULT NULL COMMENT '手機(jī)所在城市',
`service` varchar(20) DEFAULT NULL COMMENT '服務(wù)商',
`area_number` int(10) DEFAULT NULL COMMENT '區(qū)號(hào)',
`code` varchar(10) DEFAULT NULL COMMENT '郵編',
PRIMARY KEY (`id`),
UNIQUE KEY `phoneFirst` (`phone_first`) USING BTREE
) ENGINE=InnoDBDEFAULT CHARSET=utf8 COMMENT='電話區(qū)域表';
2.1 批量插入
適用于海量數(shù)據(jù)插入到數(shù)據(jù)庫的情況捶牢,用來提高寫入速度,建議上限5000
巍耗。
Java方法:
int insertBatch(@Param("list") List<CMPhone> list);
xml:
<insert id="insertBatch">
insert into c_m_phone(phone_first, phone_provence, phone_city, service, area_number, code)
values
<foreach collection="list" item="item" separator=",">
(
#{item.phoneFirst}, #{item.phoneProvence}, #{item.phoneCity},#{item.service}, #{item.areaNumber}, #{item.code}
)
</foreach>
</insert>
2.2 流式查詢
Java方法:
/**
* 流式查詢數(shù)據(jù)
* @param model 參數(shù)
* @param handler 結(jié)果集
* @return void
*/
void findByStream(@Param("model") CMPhone model, ResultHandler handler);
xml:
<resultMap id="BaseResultMap" type="com.tmp.demo.mysql.mapper.bean.CMPhone">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="phone_first" property="phoneFirst" jdbcType="VARCHAR"/>
<result column="phone_provence" property="phoneProvence" jdbcType="VARCHAR"/>
<result column="phone_city" property="phoneCity" jdbcType="VARCHAR"/>
<result column="service" property="service" jdbcType="VARCHAR"/>
<result column="area_number" property="areaNumber" jdbcType="INTEGER"/>
<result column="code" property="code" jdbcType="VARCHAR"/>
</resultMap>
<select id="findByStream" resultMap="BaseResultMap" resultSetType="FORWARD_ONLY" fetchSize="-2147483648">
select id, phone_first, phone_provence, phone_city, service, area_number, code
from c_m_phone
<where>
<if test="model.phoneFirst != null and model.phoneFirst != '' ">
AND phone_first like #{model.phoneFirst}
</if>
</where>
</select>
應(yīng)用:
CMPhone model = new CMPhone();
model.setPhoneFirst("%37%");
cmPhoneMapper.findByStream(model, (ResultHandler<CMPhone>) resultContext -> {
System.out.println("第幾個(gè):" + resultContext.getResultCount());
CMPhone cmPhone = resultContext.getResultObject();
System.out.println("查詢結(jié)果:" + cmPhone);
});
2.3 自定義事務(wù)
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
/**
* 自定義事務(wù)測(cè)試
*/
public void transactionTest() {
// 開啟事務(wù)
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// ----------執(zhí)行數(shù)據(jù)庫操作----------start
List<CMPhone> list = new ArrayList<>();
CMPhone cmPhone = new CMPhone();
cmPhone.setPhoneFirst("X1");
list.add(cmPhone);
cmPhoneMapper.insertBatch(list);
// 如事務(wù)寫入的數(shù)據(jù)很多秋麸,且執(zhí)行時(shí)間太長,可中途進(jìn)行資源刷新(刷新緩存到數(shù)據(jù)庫炬太,提高速度)
transactionStatus.flush();
cmPhoneMapper.insertBatch(list);
// ----------執(zhí)行數(shù)據(jù)庫操作----------end
// 提交事務(wù)
transactionManager.commit(transactionStatus);
} catch (Exception e) {
e.printStackTrace();
// 回滾事務(wù)
transactionManager.rollback(transactionStatus);
}
}