Spring整合MongoDb
随着数据量的不断上涨,项目需要快速处理数据成为了第一要务,对于数据统计的严格性要求不高。
MongoDb应运而生,MongoDb是典型的文档性的数据库,对于保存多层级的数据比较方便,同时MongoDb更强调用户的访问速度,采用的是若一致性,对于数据请求提供一个“大约”的数字,以求更快处理数据。
MongoDb中内置GridFs,支持大容量的存储,同时也包含了Sharding,提供了基于Range的AUTO Sharding机制。
一个集合可按照记录的范围,分为若干的段,切分到不同的shard上。配合Replica sets实现Sharding+fail-over,不同的Sharding之间支持负载均衡。
所以说mongoDb是快速处理大数据的一个选择。
Spring Data MongoDb
Spring Data其实是一个高级别的Spring Source项目,而Spring Data MongoDB仅仅是其中的一个子项目。
- 导入MongoDb的jar依赖
<!-- spring整合MongoDB -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${spring.data.version}</version>
</dependency>
- 创建MongoDb映射的实体类(pojo)
@Data
@Document(collection = "coll_user")
public class UserInfo implements Serializable {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
// 主键使用此注解
@Id
private String id;
// 字段使用此注解
@Field
private String name;
// 字段还可以用自定义名称
@Field("myage")
private int age;
// 还可以生成索引
@Indexed(name = "index_birth", direction = IndexDirection.DESCENDING)
@Field
private Timestamp birth;
}
类名上加@Data注解,导入依赖:lombok.Data,自动实现生成Getter,Setter,equals,hashCode,toString方法
@Document - 用于类,以表示这个类需要映射到数据库,您也可以指定映射到数据库的集合名称,上面添加了集合的名称
@Field - 用于字段,并描述字段的名称,因为它将在MongoDB BSON文档中表示,允许名称与该类的字段名不同。
@Indexed - 用于字段,表示该字段需要如何创建索引
@DBRef - 用于字段,以表示它将使用com.mongodb.DBRef进行存储。
- 创建Spring-mongo.xml的配置文件
或者直接再spring配置文件中整合即可
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 读取属性文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 启用注解支持 -->
<context:annotation-config />
<!-- 扫描组件包 -->
<context:component-scan base-package="com.jastar.demo" />
<!-- SpringData类型转换器 -->
<mongo:mapping-converter id="mongoConverter">
<mongo:custom-converters>
<mongo:converter>
<bean class="com.jastar.demo.converter.TimestampConverter" />
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
<!--
MongoDB配置部分
1.mongo:连接配置
2.db-factory:相当于sessionFactory
3.mongoTemplate:与数据库接口交互的主要实现类
-->
<mongo:mongo host="${mongo.host}" port="${mongo.port}">
<mongo:options
connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
connect-timeout="${mongo.connectTimeout}"
max-wait-time="${mongo.maxWaitTime}"
auto-connect-retry="${mongo.autoConnectRetry}"
socket-keep-alive="${mongo.socketKeepAlive}"
socket-timeout="${mongo.socketTimeout}"
slave-ok="${mongo.slaveOk}"
write-number="${mongo.writeNumber}"
write-timeout="${mongo.writeTimeout}"
write-fsync="${mongo.writeFsync}" />
</mongo:mongo>
<mongo:db-factory id="mongoDbFactory" dbname="${mongo.dbname}" mongo-ref="mongo" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mongoConverter" ref="mongoConverter" />
</bean>
</beans>
主要配置了mongo连接配置,db-factory相当于sessionFactory ,mongoTemplate与数据库接口交互的主要实现类 。
mongo:options - 用于配置一些数据库连接设置信息
mongo:db-factory - 相当于Hibernate中的SessionFactory
mongoTemplate - 非常重要,整个与数据库的交互操作全是靠他,相当于Hibernate的HibernateTemplate。
- 使用MongoTemplate对MongoDb进行CRUD
@Autowired
protected MongoTemplate mgt;
首先注入MongoTemplate
public abstract class BaseDaoImpl<T> implements IBaseDao<T> {
protected abstract Class<T> getEntityClass();
@Autowired
protected MongoTemplate mgt;
@Override
public void save(T entity) {
mgt.save(entity);
}
@Override
public void update(T entity) {
// 反向解析对象
Map<String, Object> map = null;
try {
map = parseEntity(entity);
} catch (Exception e) {
e.printStackTrace();
}
// ID字段
String idName = null;
Object idValue = null;
// 生成参数
Update update = new Update();
if (EmptyUtil.isNotEmpty(map)) {
for (String key : map.keySet()) {
if (key.indexOf("{") != -1) {
// 设置ID
idName = key.substring(key.indexOf("{") + 1, key.indexOf("}"));
idValue = map.get(key);
} else {
update.set(key, map.get(key));
}
}
}
mgt.updateFirst(new Query().addCriteria(where(idName).is(idValue)), update, getEntityClass());
}
@Override
public void delete(Serializable... ids) {
if (EmptyUtil.isNotEmpty(ids)) {
for (Serializable id : ids) {
mgt.remove(mgt.findById(id, getEntityClass()));
}
}
}
@Override
public T find(Serializable id) {
return mgt.findById(id, getEntityClass());
}
@Override
public List<T> findAll() {
return mgt.findAll(getEntityClass());
}
@Override
public List<T> findAll(String order) {
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isEmpty(orderList)) {
return findAll();
}
return mgt.find(new Query().with(new Sort(orderList)), getEntityClass());
}
@Override
public List<T> findByProp(String propName, Object propValue) {
return findByProp(propName, propValue, null);
}
@Override
public List<T> findByProp(String propName, Object propValue, String order) {
Query query = new Query();
// 参数
query.addCriteria(where(propName).is(propValue));
// 排序
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isNotEmpty(orderList)) {
query.with(new Sort(orderList));
}
return mgt.find(query, getEntityClass());
}
@Override
public List<T> findByProps(String[] propName, Object[] propValue) {
return findByProps(propName, propValue, null);
}
@Override
public List<T> findByProps(String[] propName, Object[] propValue, String order) {
Query query = createQuery(propName, propValue, order);
return mgt.find(query, getEntityClass());
}
@Override
public T uniqueByProp(String propName, Object propValue) {
return mgt.findOne(new Query(where(propName).is(propValue)), getEntityClass());
}
@Override
public T uniqueByProps(String[] propName, Object[] propValue) {
Query query = createQuery(propName, propValue, null);
return mgt.findOne(query, getEntityClass());
}
@Override
public PageModel<T> pageAll(int pageNo, int pageSize) {
return pageAll(pageNo, pageSize, null);
}
@Override
public PageModel<T> pageAll(int pageNo, int pageSize, String order) {
return pageByProp(pageNo, pageSize, null, null, order);
}
@Override
public PageModel<T> pageByProp(int pageNo, int pageSize, String param, Object value) {
return pageByProp(pageNo, pageSize, param, value, null);
}
@Override
public PageModel<T> pageByProp(int pageNo, int pageSize, String param, Object value, String order) {
String[] params = null;
Object[] values = null;
if (EmptyUtil.isNotEmpty(param)) {
params = new String[] { param };
values = new Object[] { value };
}
return pageByProps(pageNo, pageSize, params, values, order);
}
@Override
public PageModel<T> pageByProps(int pageNo, int pageSize, String[] params, Object[] values) {
return pageByProps(pageNo, pageSize, params, values, null);
}
@Override
public PageModel<T> pageByProps(int pageNo, int pageSize, String[] params, Object[] values, String order) {
// 创建分页模型对象
PageModel<T> page = new PageModel<>(pageNo, pageSize);
// 查询总记录数
int count = countByCondition(params, values);
page.setTotalCount(count);
// 查询数据列表
Query query = createQuery(params, values, order);
// 设置分页信息
query.skip(page.getFirstResult());
query.limit(page.getPageSize());
// 封装结果数据
page.setList(mgt.find(query, getEntityClass()));
return page;
}
@Override
public int countByCondition(String[] params, Object[] values) {
Query query = createQuery(params, values, null);
Long count = mgt.count(query, getEntityClass());
return count.intValue();
}
/**
* 创建带有where条件(只支持等值)和排序的Query对象
*
* @param params
* 参数数组
* @param values
* 参数值数组
* @param order
* 排序
* @return Query对象
*/
protected Query createQuery(String[] params, Object[] values, String order) {
Query query = new Query();
// where 条件
if (EmptyUtil.isNotEmpty(params) && EmptyUtil.isNotEmpty(values)) {
for (int i = 0; i < params.length; i++) {
query.addCriteria(where(params[i]).is(values[i]));
}
}
// 排序
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isNotEmpty(orderList)) {
query.with(new Sort(orderList));
}
return query;
}
/**
* 解析Order字符串为所需参数
*
* @param order
* 排序参数,如[id]、[id asc]、[id asc,name desc]
* @return Order对象集合
*/
protected List<Order> parseOrder(String order) {
List<Order> list = null;
if (EmptyUtil.isNotEmpty(order)) {
list = new ArrayList<Order>();
// 共有几组排序字段
String[] fields = order.split(",");
Order o = null;
String[] item = null;
for (int i = 0; i < fields.length; i++) {
if (EmptyUtil.isEmpty(fields[i])) {
continue;
}
item = fields[i].split(" ");
if (item.length == 1) {
o = new Order(Direction.ASC, item[0]);
} else if (item.length == 2) {
o = new Order("desc".equalsIgnoreCase(item[1]) ? Direction.DESC : Direction.ASC, item[0]);
} else {
throw new RuntimeException("排序字段参数解析出错");
}
list.add(o);
}
}
return list;
}
/**
* 将对象的字段及值反射解析为Map对象<br>
* 这里使用Java反射机制手动解析,并且可以识别注解为主键的字段,以达到根据id进行更新实体的目的<br>
* key:字段名称,value:字段对应的值
*
* @param t
* 要修改的对象
* @return Map对象,注意:id字段的key封装为“{id字段名称}”,以供后续识别
* @throws Exception
*/
protected Map<String, Object> parseEntity(T t) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
/*
* 解析ID
*/
String idName = "";
Field[] declaredFields = getEntityClass().getDeclaredFields();
for (Field field : declaredFields) {
if (field.isAnnotationPresent(Id.class)) {
field.setAccessible(true);
map.put("{" + field.getName() + "}", field.get(t));
idName = field.getName();
break;
}
}
/*
* 解析其他属性
*/
Method[] methods = getEntityClass().getDeclaredMethods();
if (EmptyUtil.isNotEmpty(methods)) {
for (Method method : methods) {
if (method.getName().startsWith("get") && method.getModifiers() == Modifier.PUBLIC) {
String fieldName = parse2FieldName(method.getName());
if (!fieldName.equals(idName)) {
map.put(fieldName, method.invoke(t));
}
}
}
}
return map;
}
/**
* 将get方法名转换为对应的字段名称
*
* @param methodName
* 如:getName
* @return 如:name
*/
private String parse2FieldName(String methodName) {
String name = methodName.replace("get", "");
name = name.substring(0, 1).toLowerCase() + name.substring(1);
return name;
}
}