1.认识Spring Data MongoDB
之前还的确不知道Spring连集成Nosql的东西都实现了,还以为自己又要手动封装一个操作MongoDB的API呢,结果就发现了Spring Data MongoDB。不愧是Spring,真是给了我们春天,佩服的渣渣我目瞪狗呆……
废话少说,看招。Spring Data其实是一个高级别的Spring Source项目,而Spring Data MongoDB仅仅是其中的一个子项目。Spring Data旨在为关系型数据库、非关系型数据、Map-Reduce框架、云数据服务等等提供统一的数据访问API。
无论是哪种持久化存储, 数据访问对象(或称作为DAO,即Data Access Objects)通常都会提供对单一域对象的CRUD (创建、读取、更新、删除)操作、查询方法、排序和分页方法等。Spring Data则提供了基于这些层面的统一接口(CrudRepository,PagingAndSortingRepository)以及对持久化存储的实现。
Spring Data 包含多个子项目:
Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化
Hadoop - 基于 Spring 的 Hadoop 作业配置和一个 POJO 编程模型的 MapReduce 作业
Key-Value - 集成了 Redis 和 Riak ,提供多个常用场景下的简单封装
Document - 集成文档数据库:CouchDB 和 MongoDB 并提供基本的配置映射和资料库支持
Graph - 集成 Neo4j 提供强大的基于 POJO 的编程模型
Graph Roo AddOn - Roo support for Neo4j
JDBC Extensions - 支持 Oracle RAD、高级队列和高级数据类型
JPA - 简化创建 JPA 数据访问层和跨存储的持久层功能
Mapping - 基于 Grails 的提供对象映射框架,支持不同的数据库
Examples - 示例程序、文档和图数据库
Guidance - 高级文档
2. HelloWorld
好了,说了这么多,还是用代码表现最为实在,老规矩,Hello World,我这里创建了一个Maven Java项目demo。
2.1 配置依赖
【pom.xml】
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jastar</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <url>http://www.jastar-wang.tech</url> <!-- 版本配置 --> <properties> <spring.version>4.1.4.RELEASE</spring.version> <spring.data.version>1.7.0.RELEASE</spring.data.version> <log4j.version>1.2.17</log4j.version> </properties> <dependencies> <!-- 单元测试包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <!-- spring核心包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- 关系型数据库整合时需配置 如hibernate jpa等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <!-- spring aop 关联 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- spring整合MongoDB --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>${spring.data.version}</version> </dependency> </dependencies> <repositories> <repository> <id>spring-milestone</id> <name>Spring Maven MILESTONE Repository</name> <url>http://repo.spring.io/libs-milestone</url> </repository> </repositories> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
2.2 映射实体类
package com.jastar.demo.entity; import java.io.Serializable; import java.sql.Timestamp; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.IndexDirection; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; /** * 用户实体类 * <p> * ClassName: UserInfo * </p> * <p> * Description:本类用来展示MongoDB实体类映射的使用 * </p> * <p> * Copyright: (c)2017 Jastar·Wang,All rights reserved. * </p> * * @author Jastar·Wang * @date 2017年4月12日 */ @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; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Timestamp getBirth() { return birth; } public void setBirth(Timestamp birth) { this.birth = birth; } }
附录:
@Id - 用于字段级别,标记这个字段是一个主键,默认生成的名称是“_id”
@Document - 用于类,以表示这个类需要映射到数据库,您也可以指定映射到数据库的集合名称
@DBRef - 用于字段,以表示它将使用com.mongodb.DBRef进行存储。
@Indexed - 用于字段,表示该字段需要如何创建索引
@CompoundIndex - 用于类,以声明复合索引
@GeoSpatialIndexed - 用于字段,进行地理位置索引
@TextIndexed - 用于字段,标记该字段要包含在文本索引中
@Language - 用于字段,以设置文本索引的语言覆盖属性。
@Transient - 默认情况下,所有私有字段都映射到文档,此注解将会去除此字段的映射
@PersistenceConstructor - 标记一个给定的构造函数,即使是一个protected修饰的,在从数据库实例化对象时使用。构造函数参数通过名称映射到检索的DBObject中的键值。
@Value - 这个注解是Spring框架的一部分。在映射框架内,它可以应用于构造函数参数。这允许您使用Spring表达式语言语句来转换在数据库中检索的键值,然后再用它来构造一个域对象。为了引用给定文档的属性,必须使用以下表达式:@Value("#root.myProperty"),root要指向给定文档的根。
@Field - 用于字段,并描述字段的名称,因为它将在MongoDB BSON文档中表示,允许名称与该类的字段名不同。
@Version - 用于字段锁定,保存操作时检查修改。初始值是0,每次更新时自动触发。
2.3 整合配置
【db.properties】
###---The mongodb settings--- mongo.dbname=demo mongo.host=localhost mongo.port=27017 mongo.connectionsPerHost=8 mongo.threadsAllowedToBlockForConnectionMultiplier=4 mongo.connectTimeout=1000 mongo.maxWaitTime=1500 mongo.autoConnectRetry=true mongo.socketKeepAlive=true mongo.socketTimeout=1500 mongo.slaveOk=true mongo.writeNumber=1 mongo.writeTimeout=0 mongo.writeFsync=true
【spring-mgo.xml】
<?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:options - 用于配置一些数据库连接设置信息
mongo:db-factory - 相当于Hibernate中的SessionFactory
mongoTemplate - 非常重要,整个与数据库的交互操作全是靠他,相当于Hibernate的HibernateTemplate
另外,以上配置中有一个类型转换器,因为Spring Data MongoDB本身默认时间类型是java.util.Date,如果实体字段含有java.sql.Timestamp类型,需要自定义转换器进行转换,否则后续操作会报错(深有感触)!什么?不把转换器代码贴出来?别着急,文章最后有整个项目地址,去里面找吧……偷笑.GIF
2.4 Dao层参考实现
【BaseDaoImpl.java】
package com.jastar.demo.dao.impl; import static org.springframework.data.mongodb.core.query.Criteria.where; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.annotation.Id; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import com.jastar.demo.dao.IBaseDao; import com.jastar.demo.util.EmptyUtil; import com.jastar.demo.util.PageModel; /** * 基本操作接口MongoDB数据库实现类 * <p> * ClassName: BaseDaoImpl * </p> * <p> * Description:本实现类适用于MongoDB数据库,以下代码仅供参考,本人水平有限,可能会存在些许问题(如有更好方案可告知我,一定虚心学习), * 再次提醒,仅供参考!! * </p> * <p> * Copyright: (c)2017 Jastar·Wang,All rights reserved. * </p> * * @author Jastar·Wang * @date 2017年4月12日 */ 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; } }
说明:
以上实现仅供参考某些实现点,mongoTemplate如何使用还要自己去详细的摸索,我也没办法,毕竟我也这样过来的,大多数方法都已经提供了,只是update方法不像关系型数据库那样,给个实体类就能更新,需要自己去想办法搞定。
【TestUseService.java】
package com.jastar.test; import java.sql.Timestamp; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.jastar.demo.entity.UserInfo; import com.jastar.demo.service.UserService; import com.jastar.demo.util.PageModel; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-mgo.xml") public class TestUserService { @Autowired private UserService service; @Test public void save() { UserInfo user = new UserInfo(); user.setName("张三"); user.setAge(25); user.setBirth(Timestamp.valueOf("2017-4-12 16:52:00")); service.save(user); System.out.println("已生成ID:" + user.getId()); } @Test public void find() { UserInfo user = service.find("58edf1b26f033406394a8a61"); System.out.println(user.getName()); } @Test public void update() { UserInfo user = service.find("58edf1b26f033406394a8a61"); user.setAge(18); service.update(user); } @Test public void delete() { service.delete("58edef886f03c7b0fdba51b9"); } @Test public void findAll() { List<UserInfo> list = service.findAll("age desc"); for (UserInfo u : list) { System.out.println(u.getName()); } } @Test public void findByProp() { List<UserInfo> list = service.findByProp("name", "张三"); for (UserInfo u : list) { System.out.println(u.getName()); } } @Test public void findByProps() { List<UserInfo> list = service.findByProps(new String[] { "name", "age" }, new Object[] { "张三", 18 }); for (UserInfo u : list) { System.out.println(u.getName()); } } @Test public void pageAll() { PageModel<UserInfo> page = service.pageAll(1, 10); System.out.println("总记录:" + page.getTotalCount() + ",总页数:" + page.getTotalPage()); for (UserInfo u : page.getList()) { System.out.println(u.getName()); } } @Test public void pageByProp() { PageModel<UserInfo> page = service.pageByProp(1, 10, "name", "张三"); System.out.println("总记录:" + page.getTotalCount() + ",总页数:" + page.getTotalPage()); for (UserInfo u : page.getList()) { System.out.println(u.getName()); } } }
————————————————
版权声明:本文为CSDN博主「代码与酒」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_16313365/article/details/70142729