本文主要整理JAVA开发中的一些模板代码,作为平时参考使用。
1 MongoTemplate
版本:mongoDb4.0
1.1 写入数据
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 更新数据
// ----------[组装查询条件]----------
Query query = new Query();
query.addCriteria(Criteria.where("indexNum").is(1));
// ----------[组装更新逻辑]----------
Update update = new Update();
update.set("userName", "李四");
update.set("age", 19);
// ----------[更新数据]----------
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, "t_user_info");
System.out.println("匹配数量:"+updateResult.getMatchedCount());
// 如果更新后数据和更新前一样,则getModifiedCount()返回0
System.out.println("更新数量:"+updateResult.getModifiedCount());
1.3 基本查询
Query query = new Query();
// ----------[拼接查询条件]----------
// 等于
query.addCriteria(Criteria.where("delFlag").is("0"));
// 字符串模糊查询
query.addCriteria(Criteria.where("userName").regex("^.*张三.*$"));
// 数值比较(大于)
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");
// ----------[分页]----------
// 当前页数
int pageNumber = 1;
// 每页数量
int pageLimit = 10;
query.skip((pageNumber - 1) * pageLimit).limit(pageLimit);
// ----------[执行查询]----------
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
// 输出内容:【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)错误场景
mongoTemplate的query在组装查询条件时,执行query.addCriteria
,不能对统一字段执行多次此操作,否则会抛出InvalidMongoDbApiUsageException
异常。
错误代码,如下:
Query query = new Query();
// 对字段age,添加多个条件
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
同一个字段,拼接多个条件。
Query query = new Query();
query.addCriteria(Criteria.where("age").gte(20).lte(30));
List<Document> list = mongoTemplate.find(query, Document.class, "t_user_info");
// 输出内容:【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");
// 输出内容:【Query: { "$and" : [{ "age" : { "$gte" : 20}}, { "age" : { "$lte" : 30}}]}, Fields: {}, Sort: {}】
System.out.println(query);
1.5 游标查询
试用于海量数据查询
// ----------[组装查询条件]----------
Query query = new Query();
query.addCriteria(Criteria.where("sex").is("男"));
// ----------[游标查询]----------
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 执行业务方法
}
}catch (Exception e){
e.printStackTrace();
}
1.6 聚合查询
// ----------[分页相关]----------
// 页数
long pageNumber = 1;
// 每页数量
long pageLimit = 10;
// ----------[查询条件相关]----------
// 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),
// 映射字段到前台
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规定了aggregate管道聚合的返回数据不能超过`16M`,超过16M就会报异常错误。解决方法就是设置`allowDiskUse:true`,即允`许使用磁盘缓存`
aggregation = aggregation.withOptions(Aggregation.newAggregationOptions().allowDiskUse(true).build());
// 输出内容:【{ "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 注意事项
Map注意事项:
- 在map函数中,如果要引用当前文档自身,可以使用
this
- 在一个map函数中可以任意
多次调用emit函数
来输出具有key/value形式的中间数据
Reduce注意事项:
- 如果
一个key只有一个value
,那么MongoDB不调用
reduce函数 - 对于
同一个key
,reduce函数可能会执行多次
1.7.2 mongo脚本
(1)输出结果到实际的表
输出结果到表中(以替换整张表的方式)
// 自定义map函数
var myMap = function() {
emit(this.sex, this.age);
}
// 自定义reduce函数
var myReduce = function(key, values) {
var totalAge = 0;
for (var i = 0; i < values.length; i++) {
totalAge = totalAge + values[i];
}
return totalAge;
}
// 执行mapReduce方法
db.t_user_info.mapReduce(
// 自定义map函数
myMap,
// 自定义reduce函数
myReduce,
{
// 查询条件
query: {"delFlag" : "0"},
// 输出方式(整个替换输出到表)(输出到内容中,可用:"out: { inline: 1 }")
out: "map_reduce_result"
}
)
执行后,输出内容如下:
/* 1 */
{
// 存储数据的集合
"result" : "map_reduce_result",
// 执行花费的时间(毫秒)
"timeMillis" : 138.0,
"counts" : {
// 满足条件被发送到map函数的文档个数
"input" : 1500,
// 在map函数中emit被调用的次数,也就是所有集合中的数据总量
"emit" : 1500,
// 满足条件被发送到reduce函数的文档个数
"reduce" : 15,
// 结果集合中的文档个数
"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"
}
}
执行结束后,表map_reduce_result中结果如下:
/* 1 */
{
"_id" : "女",
"value" : 12500.0
}
/* 2 */
{
"_id" : "男",
"value" : 19999.0
}
(2)输出到内存
如只想获取,计算结果,不想将数据结果输出到实际的表中。可以将out进行如下配置,将结果,输出到内存中
:
out: { inline: 1 }
这个选项只有在结果集单个文档大小在16MB限制范围内时才有效
完整脚本如下:
// 自定义map函数
var myMap = function() {
emit(this.sex, this.age);
}
// 自定义reduce函数
var myReduce = function(key, values) {
var totalAge = 0;
for (var i = 0; i < values.length; i++) {
totalAge = totalAge + values[i];
}
return totalAge;
}
// 执行mapReduce方法
db.t_user_info.mapReduce(
// 自定义map函数
myMap,
// 自定义reduce函数
myReduce,
{
// 查询条件
query: {"delFlag" : "0"},
// 输出方式(内存临时表)
out: { inline: 1 }
}
)
输出结果如下(可见results
属性中,可以看到计算的最终结果)
/* 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)写入到内存中
// (1)定义map函数
String mapFun = "function() {\n" +
" emit(this.sex, this.age);\n" +
"}";
// (2)定义reduce函数
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)执行mapReduce方法
MapReduceResults<Document> mapReduceResults = mongoTemplate.mapReduce(new Query(Criteria.where("delFlag").is("0")), "t_user_info", mapFun, reduceFun, Document.class);
// (4)输出执行结果
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函数
String mapFun = "function() {\n" +
" emit(this.sex, this.age);\n" +
"}";
// (2)定义reduce函数
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)定义相关属性配置
MapReduceOptions mapReduceOptions = MapReduceOptions.options().outputCollection("my_map_reduce_res");
// (4)执行mapReduce方法
MapReduceResults<Document> mapReduceResults = mongoTemplate.mapReduce(new Query(Criteria.where("delFlag").is("0")), "t_user_info", mapFun, reduceFun, mapReduceOptions, Document.class);
// (5)输出执行结果
Iterator<Document> iterator = mapReduceResults.iterator();
while (iterator.hasNext()) {
// Document{{_id=女, value=12500.0}}
// Document{{_id=男, value=19999.0}}
System.out.println(iterator.next());
}
根据输出结果,可见,仍然可在对象MapReduceResults中获取执行结果,执行结束后,查询数据库,可见目标表my_map_reduce_res中,已生成我们需要的数据。
/* 1 */
{
"_id" : "女",
"value" : 12500.0
}
/* 2 */
{
"_id" : "男",
"value" : 19999.0
}
1.8 bulk
主要适用于批量更新
,减少更新请求的调用次数。也可混合使用(写入、更新、删除)。
(1)写法1
// ----------[定义更新集合]----------
List<Pair<Query, Update>> updatePairList = new ArrayList<>();
// ----------[组装更新集合]----------
// 组装更新数据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);
// 组装更新数据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);
// ......
// ----------[更新数据]----------
BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "t_user_info");
operations.updateOne(updatePairList);
BulkWriteResult writeResult = operations.execute();
boolean result = writeResult.wasAcknowledged();
System.out.println("执行结果:" + result);
(2)写法2
// ----------[定义操作对象]----------
BulkOperations operations = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, "t_user_info");
// ----------[组装更新集合]----------
// 组装更新数据1
Query query1 = new Query();
query1.addCriteria(Criteria.where("indexNum").is(1));
Update update1 = new Update();
update1.set("delFlag", "21");
operations.updateOne(query1,update1);
// 组装更新数据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()
// ----------[更新数据]----------
BulkWriteResult writeResult = operations.execute();
boolean result = writeResult.wasAcknowledged();
System.out.println("执行结果:" + result);
1.9 切换到主库查询
默认
readPreference为ReadPreference.secondaryPreferred()
时,可通过如下方式,临时设置从主库读取数据
。
也可设置专门从主库读取数据的mongoTemplate句柄
。
// mongoTemplate切换到主库查询
try {
mongoTemplate.setReadPreference(ReadPreference.primary());
// TODO 执行查询操作......
}catch (Exception e){
// 异常(处理或抛出)
e.printStackTrace();
}finally {
// 切换回默认的ReadPreference
mongoTemplate.setReadPreference(ReadPreference.secondary());
}
2 Mybatis
应用数据库:mysql
。测试使用建表脚本如下:
CREATE TABLE `c_m_phone` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`phone_first` varchar(20) DEFAULT NULL COMMENT '前缀号段',
`phone_provence` varchar(20) DEFAULT NULL COMMENT '手机所在省份',
`phone_city` varchar(20) DEFAULT NULL COMMENT '手机所在城市',
`service` varchar(20) DEFAULT NULL COMMENT '服务商',
`area_number` int(10) DEFAULT NULL COMMENT '区号',
`code` varchar(10) DEFAULT NULL COMMENT '邮编',
PRIMARY KEY (`id`),
UNIQUE KEY `phoneFirst` (`phone_first`) USING BTREE
) ENGINE=InnoDBDEFAULT CHARSET=utf8 COMMENT='电话区域表';
2.1 批量插入
适用于海量数据插入到数据库的情况,用来提高写入速度,建议上限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方法:
/**
* 流式查询数据
* @param model 参数
* @param handler 结果集
* @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>
应用:
CMPhone model = new CMPhone();
model.setPhoneFirst("%37%");
cmPhoneMapper.findByStream(model, (ResultHandler<CMPhone>) resultContext -> {
System.out.println("第几个:" + resultContext.getResultCount());
CMPhone cmPhone = resultContext.getResultObject();
System.out.println("查询结果:" + cmPhone);
});
2.3 自定义事务
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
/**
* 自定义事务测试
*/
public void transactionTest() {
// 开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// ----------执行数据库操作----------start
List<CMPhone> list = new ArrayList<>();
CMPhone cmPhone = new CMPhone();
cmPhone.setPhoneFirst("X1");
list.add(cmPhone);
cmPhoneMapper.insertBatch(list);
// 如事务写入的数据很多,且执行时间太长,可中途进行资源刷新(刷新缓存到数据库,提高速度)
transactionStatus.flush();
cmPhoneMapper.insertBatch(list);
// ----------执行数据库操作----------end
// 提交事务
transactionManager.commit(transactionStatus);
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback(transactionStatus);
}
}