文章目录
- 开干
- 整体文件结构如下
- 创建springBoot项目,pom文件中添加相关依赖
- yaml文件中配置mongo地址
- 准备一个实体类
- 通过MongoTemplate查询
- 通过MongoRepository的方式
- 查询所有
- 新增数据
- 修改数据
- 分页查询
- 自定义的条件查询
一些废话:
上一篇写了java链接MongoDB,但实际项目中应该很少用java直接连接了,都是在框架下进行的,所以在框架里能crud才是好的crud小能手
开干
整体文件结构如下
创建springBoot项目,pom文件中添加相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mongoDB的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
yaml文件中配置mongo地址
server:
port: 8090
spring:
application:
name: mongo
data:
mongodb:
# 端口默认是27017
host: 192.168.0.108
database: common_test
准备一个实体类
!!注意:
如下
package com.dean.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Comment implements Serializable {
//ps:名称要完全和mongo中的一致
private String _id;
private String content;
private String userId;
private Double thumbUp;
}
通过MongoTemplate查询
实际上,在框架里使用Mongo进行查询有两种方式(一种就是通过MongoTemplate,另一种是通过三层架构,只是在dao层需要继承MongoRepository),MongoTemplate的方式可以使用Mongo-driver的原生API
我们写个查询浅试一下
package com.dean.controller;
import com.dean.entity.Comment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/mongo")
public class MongoController {
//在此放弃@Autowired的方式强制注入,采用构造器方式进行注入
private final MongoTemplate mongoTemplate;
public MongoController(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
//查询所有
//可以通过mongoTemplate使用Mongo-driver的原生API
@GetMapping("/findAll")
public List<Comment> findAllComments() {
//以下注释代码说明可以获得Mongo-driver的原生对象,既然有了原生,那么所有在java里可以做的,在这里也可以做
// MongoCollection<Document> collection = mongoTemplate.getCollection("comment");
return mongoTemplate.findAll(Comment.class);
}
}
启动程序后,使用postman进行测试
可以看到非常便捷就查到了
通过MongoRepository的方式
该方式需要搭建我们熟悉的三层架构
我们从功能入手说
查询所有
- controller层
//在此放弃@Autowired的方式强制注入,采用构造器方式进行注入
//2、使用三层架构的方式
private final CommentService commentService;
public MongoController(CommentService commentService) {
this.commentService = commentService;
}
//三层架构方式_查询所有
@GetMapping("/findAllByService")
public List<Comment> findAllCommentsByService() {
return commentService.findAllComments();
}
- service层
//注入repository
private final CommentRepository commentRepository;
public CommentService(CommentRepository commentRepository) {
this.commentRepository = commentRepository;
}
public List<Comment> findAllComments() {
return commentRepository.findAll();
}
- repository层
@Repository
//MongoRepository<Comment,String> 说明查询的是Comment这个集合 查询语句的类型
public interface CommentRepository extends MongoRepository<Comment,String> {
}
- postman测试
新增数据
- 首先需要一个可以生成id的工具类
package com.dean.util;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/**
* <p>名称:IdWorker.java</p>
* <p>描述:分布式自增长ID</p>
* <pre>
* Twitter的 Snowflake JAVA实现方案
* </pre>
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
* <p>
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
*
* @author Polim
*/
public class IdWorker {
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
private final static long twepoch = 1288834974657L;
// 机器标识位数
private final static long workerIdBits = 5L;
// 数据中心标识位数
private final static long datacenterIdBits = 5L;
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 数据中心ID最大值
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 数据中心ID左移17位
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 上次生产id时间戳 */
private static long lastTimestamp = -1L;
// 0,并发控制
private long sequence = 0L;
private final long workerId;
// 数据标识id部分
private final long datacenterId;
public IdWorker(){
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId
* 工作机器ID
* @param datacenterId
* 序列号
*/
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/**
* <p>
* 获取 maxWorkerId
* </p>
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* <p>
* 数据标识id部分
* </p>
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1])
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e) {
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
public static void main(String[] args) {
IdWorker idWorker=new IdWorker(0,0);
for(int i=0;i<10000;i++){
long nextId = idWorker.nextId();
System.out.println(nextId);
}
}
}
- controller层
@PostMapping("/insertDoc")
public Comment insertDoc(@RequestBody Comment comment) {
//设定id (虽然会生成ObjectID,但由于不可控,所以自己设定)
comment.set_id(String.valueOf(new IdWorker().nextId()));
return commentService.addDoc(comment);
}
- Service层
public Comment addDoc(Comment comment) {
Comment insert = commentRepository.insert(comment);
return insert;
}
- repository层
因为insert实际是父类MongoRepository的方法,所以repository层代码同 查询所有的repository层(以下如果说同上,意思就是同 查询所有的 repository层) - Postman测试
修改数据
因为MongoRepository未提供update方法,所以我们在此使用原生的MongoTemplate自己编写
- 只需要Controller完成即可
//修改数据(由于MongoRepository没有提供update方法,所以需要通过MongoTemplate自己写)
@PutMapping("/updateDoc/{id}")
public String updateDoc(@PathVariable("id") String id,
@RequestBody Comment comment) {
comment.set_id(id);
//条件组装器
Criteria criteria = Criteria.where("_id").is(id);
Query query = Query.query(criteria);
//更新的字段
Update update = new Update();
update.set("content", comment.getContent());
update.set("testField", "不知道能不能新增一个字段");
UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Comment.class);
return updateResult.getModifiedCount() + "条数据被修改";
}
- postman测试
分页查询
- Controller层
//三层架构_分页查询
@GetMapping("/findPage/{current}/{size}")
public List<Comment> findPage(@PathVariable("current") Integer current,
@PathVariable("size") Integer size) {
return commentService.findByPage(current, size);
}
- Service层
public List<Comment> findByPage(Integer current, Integer size) {
//Pageable不是mongo的,是SpringBoot提供的分页器
Pageable pageable = PageRequest.of(current, size);
Page<Comment> commentPage = commentRepository.findAll(pageable);
return commentPage.getContent();
}
- Repository层
同上 - Postman测试
自定义的条件查询
- Controller层
//三层架构_自定义查询
@GetMapping("/findByCustom")
public List<Comment> findByCustom(@RequestParam String word) {
return commentService.findByCustom(word);
}
- Service层
public List<Comment> findByCustom(String word) {
return commentRepository.findByContentContaining(word);
}
- Repository层
@Repository
//MongoRepository<Comment,String> 说明查询的是Comment这个集合 查询语句的类型
public interface CommentRepository extends MongoRepository<Comment,String> {
//除了一些本身提供的接口,也支持自定义查询
//以下方法的含义是查询Comment表中Content字段包含word的数据
List<Comment> findByContentContaining(String word);
}
- Postman测试
以上。