spring-data-mongodb两种实现方式对比

版权声明:欢迎转载 https://blog.csdn.net/feinifi/article/details/83752862

  之前介绍过springboot和mongodb整合,使用了spring-data-mongodb,因为springboot注解和自动化配置,我们少了很多配置,这里介绍spring整合mongodb,使用的仍然是spring-data-mongodb,这里需要配置一个spring-mongo.xml的配置文件。
  spring-data-mongodb其实有两种实现方式,一种是直接继承MongoRepository接口,dao层的实现,默认提供了CRUD的各种方法,几乎不用编写任何代码。另一种是通过MongoTemplate来操作数据库,这样需要自定义dao层接口,默认没有任何接口可以使用,但是MongoTemplate提供了操作数据库的各种CRUD接口,因此,dao层接口需要我们再次封装一次。
两种方式均需要配置mongo:clientmongo:db-factorymongoTemplate类
这里只整合spring+mongodb,因此构建的工程相对简单:

maven工程pom.xml配置文件:

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-mongodb</artifactId>
      <version>2.0.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.0.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>

实体定义:

BaseEntity

package com.xxx.springmongo.entity;
import java.util.Date;
import org.springframework.data.annotation.Id;
public class BaseEntity {
	@Id
	protected String id;
	protected Date createDate;
	protected Date modifyDate;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	public Date getModifyDate() {
		return modifyDate;
	}
	public void setModifyDate(Date modifyDate) {
		this.modifyDate = modifyDate;
	}
}

User

package com.xxx.springmongo.entity;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="xx_user")
public class User extends BaseEntity{
	private String username;
	private String password;
	private String mobile;
	@DBRef
	private Role role;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getMobile() {
		return mobile;
	}
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}
	public void setRole(Role role) {
		this.role = role;
	}
	public Role getRole() {
		return role;
	}
	@Override
	public String toString() {
		return "{\"id\":"+id+",\"username\":"+username+",\"password\":"+
		password+",\"mobile\""+mobile+",\"role\":"+role+"}";
	}
}

Role

package com.xxx.springmongo.entity;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="xx_role")
public class Role extends BaseEntity{
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "{\"id\":"+id+",\"name\":"+name+"}";
	}
}

实体之间的关系,用户(User),角色(Role)均继承BaseEntity,另外用户有一个角色的引用。通过注解@DBRef来指定他们在数据库中的关系。

配置文件

mongo.properties

mongo.host=127.0.0.1
mongo.port=27017
mongo.db=springmongo

spring-mongo.xml 核心配置(需要引入spring-mongo.xsd)

<context:property-placeholder location="classpath:mongo.properties"/>
<context:component-scan base-package="com.xxx.springmongo">

</context:component-scan>
<mongo:mongo-client id="mongo-client" host="${mongo.host}" 
port="${mongo.port}">
    <mongo:client-options/>
</mongo:mongo-client>
 
<mongo:repositories base-package="com.xxx.springmongo.dao"/>

<mongo:db-factory dbname="${mongo.db}" mongo-ref="mongo-client"/>
<bean id="mongoTemplate" 
class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>

第一种实现方式:dao层接口继承MongoRepository

UserDao

package com.xxx.springmongo.dao;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import com.xxx.springmongo.entity.Role;
import com.xxx.springmongo.entity.User;
public interface UserDao extends MongoRepository<User, String>{
	public User findByRole(Role role);	
	@Query(value="{'role.$id': ?0 }")
	public User findByRoleId(ObjectId id);
}

RoleDao

package com.xxx.springmongo.dao;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.xxx.springmongo.entity.Role;
public interface RoleDao extends MongoRepository<Role, String>{
}

默认情况下,我们只是定义了dao层的用户接口和角色接口,并没有实现相关方法。UserDao中为了实现通过关联角色查询用户,增加了两个接口。
服务层接口:
RoleService

package com.xxx.springmongo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Service;
import com.xxx.springmongo.dao.RoleDao;
import com.xxx.springmongo.entity.Role;
@Service
public class RoleService {
	@Autowired
	private RoleDao roleDao;
	public Role save(Role role) {
		return roleDao.save(role);
	}
	
	public Role findById(String id) {
		return roleDao.findById(id).get();
	}
	
	public Role findByName(String name) {
		Role role = new Role();
		role.setName(name);
		ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", match->match.contains());
		Example<Role> example = Example.of(role, matcher);
		return roleDao.findOne(example).get();
	}
}

UserService

package com.xxx.springmongo.service;
import java.util.List;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xxx.springmongo.dao.UserDao;
import com.xxx.springmongo.entity.Role;
import com.xxx.springmongo.entity.User;
@Service
public class UserService {
	@Autowired
	private UserDao userDao;	
	public User save(User user) {
		return userDao.save(user);
	}
	public User findById(String id) {
		return userDao.findById(id).get();
	}
	public List<User> findAll(){
		return userDao.findAll();
	}
	public User findByRole(Role role) {
		return userDao.findByRoleId(new ObjectId(role.getId()));
	}
}

测试类:

AppTest

package com.xxx.springmongo;
import java.util.Date;
import org.junit.Assert;
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.xxx.springmongo.entity.Role;
import com.xxx.springmongo.entity.User;
import com.xxx.springmongo.service.RoleService;
import com.xxx.springmongo.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring-mongo.xml")
public class AppTest {
	@Autowired
	private UserService userService;
	@Autowired
	private RoleService roleService;
	
	@Test
	public void save() {
		Role role = new Role();
		role.setName("superadmin");
		role.setCreateDate(new Date());
		role.setModifyDate(new Date());
		Role save = roleService.save(role);
		Assert.assertEquals("superadmin", save.getName());
	}
	
	@Test
	public void query() {
		Role role = roleService.findByName("admin");
		System.out.println(role);
		//Assert.assertEquals("admin", role.getName());
		User user = new User();
		user.setCreateDate(new Date());
		user.setUsername("admin");
		user.setPassword("admin");
		user.setMobile("15011186302");
		user.setModifyDate(new Date());
		user.setRole(role);
		User tmp = userService.save(user);
		Assert.assertEquals("admin", tmp.getUsername());
	}
	
	@Test
	public void query1() {
		Role role = roleService.findByName("admin");
		User tmp = userService.findByRole(role);
		System.out.println(tmp);
		Assert.assertEquals("admin", tmp.getUsername());
	}
}

以上测试用例只写了添加角色,添加用户,按照角色名查找角色,按照角色查找用户。其中按照角色查找用户是一个关联查询,在MongoRepository的实现方式中有两种实现:

  • 第一种是和普通的按照属性查找的方式实现。
  • 第二种是稍微有一点改变,就是模拟在mongo-shell下通过关联属性role字段的$id来查询
> db.xx_user.find({"role.$id":new ObjectId("5bdfbbc2e6c87e4f147f7fab")})
{ "_id" : ObjectId("5bdfed63e6c87e569c9f8865"), 
"username" : "admin",
"password" : "admin", 
"mobile" : "15011186302", 
"role" : DBRef("xx_role", 
ObjectId("5bdfbbc2e6c87e4f147f7fab")), 
"createDate" : ISODate("2018-11-05T07:12:35.739Z"), 
"modifyDate" : ISODate("2018-11-05T07:12:35.739Z"),
 "_class" : "com.xxx.springmongo.entity.User" }
>

第二种实现方式:通过MongoTemplate来操作数据库

IBaseDao定义一个基础接口

package com.xxx.springmongo.mongo.dao;
import java.io.Serializable;
import java.util.List;
import org.springframework.data.domain.Page;
public interface IBaseDao<T> {
	void save(T entity);
	void update(T entity);
	void delete(Serializable... ids);
	T find(Serializable id);
	List<T> findAll();
	List<T> findAll(String order);
	List<T> findByProp(String propName,Object value);
	List<T> findByProp(String propName,Object value,String order);
	List<T> findByProps(String[] propName,Object[] values);
	List<T> findByProps(String[] propName,Object[] values,String order);
	T uniqueByProp(String propName,Object value);
	T uniqueByProps(String[] propName,Object[] values);
	int countByCondition(String[] params,Object[] values);
}

UserDao

package com.xxx.springmongo.mongo.dao;
import com.xxx.springmongo.entity.User;
public interface UserDao extends IBaseDao<User>{
}

BaseDaoImpl

package com.xxx.springmongo.mongo.dao.impl;
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.Page;
import org.springframework.data.domain.Pageable;
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.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import com.xxx.springmongo.mongo.dao.IBaseDao;
public abstract class BaseDaoImpl<T> implements IBaseDao<T> {
	
	protected abstract Class<T> getEntityClass();
	@Autowired
	protected MongoTemplate mongoTemplate;

	@Override
	public void save(T entity) {
		mongoTemplate.save(entity);
	}

	@Override
	public void update(T entity) {
		Map<String, Object> map = null;
		try {
			map = parseEntity(entity);
		}catch (Exception e) {
			e.printStackTrace();
		}
		String id = null;
		Object value = null;
		Update update = new Update();
		if(map!=null && map.size()>0) {
			for(String key:map.keySet()) {
				if(key.startsWith("{")) {
					id = key.substring(key.indexOf("{")+1,key.indexOf("}"));
					value = map.get(key);
				}else {
					update.set(key, map.get(key));
				}
			}
		}
		mongoTemplate.updateFirst(new Query().addCriteria(Criteria.where(id).is(value)), update, getEntityClass());
	}

	@Override
	public void delete(Serializable... ids) {
		if(ids!=null&&ids.length>0) {
			for(Serializable id:ids) {
				mongoTemplate.remove(mongoTemplate.findById(id, getEntityClass()));
			}
		}
		
	}

	@Override
	public T find(Serializable id) {
		return mongoTemplate.findById(id, getEntityClass());
	}

	@Override
	public List<T> findAll() {
		return mongoTemplate.findAll(getEntityClass());
	}

	@SuppressWarnings("deprecation")
	@Override
	public List<T> findAll(String order) {
		List<Order> orderlist = parseOrder(order);
		if(orderlist==null||orderlist.size()==0) {
			return findAll();
		}
		
		return mongoTemplate.find(new Query().with(new Sort(orderlist)), getEntityClass());
	}

	@Override
	public List<T> findByProp(String propName, Object value) {
		return findByProp(propName, value, null);
	}

	@SuppressWarnings("deprecation")
	@Override
	public List<T> findByProp(String propName, Object value, String order) {
		Query query = new Query();
		query.addCriteria(Criteria.where(propName).is(value));
		List<Order> orderlist = parseOrder(order);
		if(orderlist!=null && orderlist.size()>0) {
			query.with(new Sort(orderlist));
		}
		return null;
	}

	@Override
	public List<T> findByProps(String[] propName, Object[] values) {
		return findByProps(propName, values, null);
	}

	@Override
	public List<T> findByProps(String[] propName, Object[] values, String order) {
		Query query = createQuery(propName, values, order);
		return mongoTemplate.find(query, getEntityClass());
	}

	@Override
	public T uniqueByProp(String propName, Object value) {
		return mongoTemplate.findOne(new Query().addCriteria(Criteria.where(propName).is(value)),getEntityClass());
	}

	@Override
	public T uniqueByProps(String[] propName, Object[] values) {
		Query query = createQuery(propName, values, null);
		return mongoTemplate.findOne(query, getEntityClass());
	}
	@Override
	public int countByCondition(String[] params, Object[] values) {
		Query query = createQuery(params, values, null);
		Long count = mongoTemplate.count(query, getEntityClass());
		return count.intValue();
	}

	
	protected Map<String, Object> parseEntity(T t) throws Exception{
		Map<String, Object> map = new HashMap<>();
		String id = "";
		Field[] declaredFields = getEntityClass().getDeclaredFields();
		for(Field field:declaredFields) {
			if(field.isAnnotationPresent(Id.class)) {
				field.setAccessible(true);
				map.put("{"+field.getName()+"}", field.get(t));
				id = field.getName();
				break;
			}
		}
		
		Method[] declaredMethods = getEntityClass().getDeclaredMethods();
		if( declaredFields != null&& declaredFields.length > 0 ) {
			for(Method method:declaredMethods) {
				if(method.getName().startsWith("get")&&method.getModifiers()==Modifier.PUBLIC) {
					String fieldName = parse2FieldName(method.getName());
					if(!fieldName.equals(id)) {
						map.put(fieldName, method.invoke(t));
					}
				}
			}
		}
		return map;
	}
	
	private String parse2FieldName(String method) {
		String name = method.replace("get", "");
		name = name.substring(0, 1).toLowerCase()+name.substring(1);
		return name;
	}
	
	@SuppressWarnings("deprecation")
	public Query createQuery(String[] propName,Object[] values,String order) {
		Query query = new Query();
		//where
		if(propName!=null&&values!=null) {
			for(int i=0;i<propName.length;i++) {
				query.addCriteria(Criteria.where(propName[i]).is(values[i]));
			}
		}
		
		List<Order> orderlist = parseOrder(order);
		if(orderlist!=null && orderlist.size()>0) {
			query.with(new Sort(orderlist));
		}
		return query;
	}
	
	public List<Order> parseOrder(String order){
		List<Order> list = null;
		if(order!=null && !"".equals(order)) {
			list = new ArrayList<>();
			String[] fields = order.split(",");
			Order o = null;
			String[] items = null;
			for(int i=0;i<fields.length;i++) {
				if(fields[i]==null) {
					continue;
				}
				items = fields[i].split(" ");
				if(items.length==1) {
					o = new Order(Direction.ASC,items[0]);
				}else if(items.length==2) {
					o = new Order("desc".equalsIgnoreCase(items[1])?Direction.DESC:Direction.ASC, items[0]);
				}else {
					throw new RuntimeException("order field parse error");
				}
				list.add(o);
			}
		}
		return list;
	}
}

UserDaoImpl

package com.xxx.springmongo.mongo.dao.impl;
import org.springframework.stereotype.Repository;
import com.xxx.springmongo.entity.User;
import com.xxx.springmongo.mongo.dao.UserDao;
@Repository
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao{
	@Override
	protected Class<User> getEntityClass() {
		return User.class;
	}
}

UserServiceImpl

package com.xxx.springmongo.mongo.service;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xxx.springmongo.entity.Role;
import com.xxx.springmongo.entity.User;
import com.xxx.springmongo.mongo.dao.UserDao;
@Service
public class UserServiceImpl {
	@Autowired
	private UserDao userDao;
	public void save(User user) {
		userDao.save(user);
	}
	public void update(User user) {
		userDao.update(user);
	}
	
	public User findById(String id) {
		return userDao.find(id);
	}
	
	public User findByRole(Role role) {
		//return userDao.uniqueByProp("role", role);
		return userDao.uniqueByProp("role.$id", new ObjectId(role.getId()));
	}
}

MongoServiceTest

package com.xxx.springmongo;
import org.junit.Assert;
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.xxx.springmongo.entity.Role;
import com.xxx.springmongo.entity.User;
import com.xxx.springmongo.mongo.service.UserServiceImpl;
import com.xxx.springmongo.service.RoleService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring-mongo.xml")
public class MongoServiceTest {
	@Autowired
	private UserServiceImpl userService;
	@Autowired
	private RoleService roleService;
	@Test
	public void query() {
		Role role = roleService.findByName("admin");
		User user = userService.findByRole(role);
		System.out.println(user);
		Assert.assertEquals("admin", user.getUsername());
	}
}

通过操作MongoTemplate来操作数据库,我们为了有一层dao,额外添加了一个公共的接口IBaseDao,然后用一个抽象的类BaseDaoImpl来实现了所有定义的接口。当我们定义具体的UserDaoImpl时,只需要继承BaseDaoImpl和实现自己的UserDao接口。这里面实现关联查询也有两种方法:

  • 和查询普通属性一样按照属性role来查询user。
  • 通过给role.$id赋值new ObjectId(id)的方式来查询。

以上给出了两种spring-data-mongodb整合dao层的实现方式。他们两个的区别在于继承MongoRepository接口时,无需实现接口,如果有特别需要的接口,比如按照某一个属性查找时,只需要在dao接口中声明即可。例如:
public User findByRole(Role role);
复杂的查询可以通过@Query来指定具体的参数,例如:
@Query(value="{‘role.$id’: ?0 }")
public User findByRoleId(ObjectId id);

这种实现方式,接口无需注解@Repository,但是需要在配置文件中加入一个配置项:<mongo:repositories base-package="com.xxx.springmongo.dao"/>,该配置项指定dao层接口所在的包。
dao层通过MongoTemplate来操作数据库的方式,需要我们自己定义需要的相关接口,然后实现,可以通过Query来指定条件,实现复杂查询。该种实现方式需要在实现接口类上注解@Repository,另外,无需配置配置文件中加入配置<mongo:repository base-package="com.xxx.springmongo.mongo.dao"/>来指定需要扫描的dao层包。关联查询时,也有两种实现方式:直接按照属性查找和给role.\$id赋值。

public User findByRole(Role role) {
	//return userDao.uniqueByProp("role", role);
	return userDao.uniqueByProp("role.$id", new ObjectId(role.getId()));
}

猜你喜欢

转载自blog.csdn.net/feinifi/article/details/83752862