见springcloud学习笔记里;
freemarker即springmvc当中使用视图转发器返回的优化版本,具体的freemarker语法见springcloud学习笔记
1、导入依赖
org.springframework.boot
spring-boot-starter-web
下面的这个是freemarker核心注解
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-test
org.projectlombok
lombok
org.apache.commons
commons-io
1.3.2
2、yaml配置
server:
port: 8881 #服务端口
spring:
application:
name: freemarker-demo #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名,不指定的话就会是ftlhtemplate-loader-path: classpath:/templates #模板存放位置
3、在resources下创建templates,此目录为freemarker的默认模板存放目录。
再里面创建第一个模板 01-basic.ftl 这里注意后缀不要写错
Hello World!
普通文本 String 展示:
Hello ${name}
对象Student中的数据展示:
姓名:${stu.name}
年龄:${stu.age}
02-list.ftl 这个是用来测试基础语法
Hello World!
<#-- list 数据的展示 -->
展示list中的stu数据:
姓名
年龄
钱包
<#if stus??> <#--stus为空报错可以加上判断如下,因为为空就会报错,所以使用stus??可以让它为空时为空白-->
<#list stus as stu>
0000
${stu.name}
${stu.age}
${stu.money}
#list>
#if>
<#list stus as stu > <#--list遍历集合-->
<#if stu.name='小红'>
${stu_index+1}
${stu.name}
${stu.age}
${stu.money}
<#else >
${stu_index+1}
${stu.name}
${stu.age}
${stu.money}
#if>
#list>
<#-- Map 数据的展示 -->
map数据的展示:
方式一:通过map['keyname'].property
输出stu1的学生信息:
姓名:${stuMap['stu1'].name}
年龄:${stuMap['stu1'].age}
方式二:通过map.keyname.property
输出stu2的学生信息:
姓名:${stuMap.stu2.name}
年龄:${stuMap.stu2.age}
遍历map中两个学生信息:
序号
姓名
年龄
钱包
<#list stuMap?keys as key>
${key_index +1}
${stuMap[key].name}
${stuMap[key].age}
${stuMap[key].money}
#list>
当前的日期为:${today?datetime}
自定义格式化:${today?string("yyyy年MM月")}
4、创建controller
@Controller
public class basicController {
@GetMapping("/basic")
public String basic(Model model){
model.addAttribute("name","许石豪");
Student stu = new Student();
stu.setName("xushihao");
stu.setAge(21);
model.addAttribute("stu",stu);
return "01-basic";
}
@GetMapping("/list")
public String list(Model model){
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
stu1.setBirthday(new Date());
//小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//将两个对象模型数据存放到List集合中
List stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//向model中存放List集合数据
model.addAttribute("stus",stus);
//创建Map数据
HashMap stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
// 3.1 向model中存放Map数据
model.addAttribute("stuMap", stuMap);
//传递日期
model.addAttribute("today",new Date());
return "02-list";
}
}
最后启动项目访问即可,需要注意的是,模板转发视图使用的是controller,而不是RestController
2、freemarker静态文件生成
@SpringBootTest(classes = FreemarkerApplication.class)
@RunWith(SpringRunner.class)
public class freemarkerTest {
@Autowired
private Configuration configuration;
/*静态化页面测试*/
@Test
public void test() throws IOException, TemplateException {
//freemarker的模板对象,获取模板
Template template = configuration.getTemplate("02-list.ftl");
Map params = getData();
//合成
//第一个参数 数据模型
//第二个参数 输出流
template.process(params, new FileWriter("d:/java/springCloud学习笔记/静态化页面/list.html"));
}
private Map getData() {
Map map = new HashMap<>();
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
stu1.setBirthday(new Date());
//小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//将两个对象模型数据存放到List集合中
List stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//向map中存放List集合数据
map.put("stus", stus);
//创建Map数据
HashMap stuMap = new HashMap<>();
stuMap.put("stu1", stu1);
stuMap.put("stu2", stu2);
//向map中存放Map数据
map.put("stuMap", stuMap);
//日期
map.put("today",new Date());
//返回Map
return map;
}
}
镜像拉取 docker pull minio/minio
这里因为docker跌打快,所以直接拉取容器命令改变
运行容器:
docker run -d \
--name minio1 \
-p 9000:9000 \
-p 9001:9001 \
--privileged=true \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=admin123" \ 用户名密码
-v /home/minio/data:/data \
-v /home/minio/config:/root/.minio \
minio/minio server \
--console-address ":9000" \ 9000浏览器访问
--address ":9001" /data 9001后端访问
springboot下依赖文件
io.minio
minio
7.1.0
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
将freemarker生成的静态文件上传文件到minio桶当中
需要注意的是,我们minio设置的的后端访问端口为9001,所以我们这里获取链接信息使用的是9001端口,后面直接通过路径访问页面也是一样,需要使用9001端口
public class MinioTest { //创建测试类,上传html文件
public static void main(String[] args) {
try{
//创建输入流,上传html文件
FileInputStream fileInputStream = new FileInputStream("D:\\java\\springCloud学习笔记\\静态化页面\\list.html");
//获取minio的链接信息 创建一个minio的客户端 用户名,密码,网址
MinioClient minioClient = MinioClient.builder().credentials("admin", "admin123").endpoint("http://192.168.200.128:9001").build();
//上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("list.html") //文件名称
.contentType("text/html")//文件类型
.bucket("leadnews")//桶名称,minio图形界面创建的名称一致
.stream(fileInputStream,fileInputStream.available(),-1) //上传文件流
.build();
minioClient.putObject(putObjectArgs);
//访问路径
System.out.println("http://192.168.200.128:9001/leadnews/list.html");
}catch (Exception e){
e.printStackTrace();
}
}
}
添加的minio配置文件就是我们minio的账号密码桶访问链接地址,和上传桶后访问的ip地址
实现文章详情新增步骤
模拟新增文章详情,文章素材发布之后将文章详情静态页面生成出来路径存储到minio当中,下次才能直接访问!
@SpringBootTest(classes = ArticleApplication.class)
@RunWith(SpringRunner.class)
public class ArticleFreemarkerTest {
@Autowired
private ApArticleContentMapper apArticleContentMapper;
@Autowired
private ArticleMapper articleMapper;
@Autowired //这个是静态文件生成所需的
private Configuration configuration;
//minio操作starter
@Autowired
private FileStorageService fileStorageService;
/*
* 模拟我们新增文章的时候生成我们的文章详情静态文章内容
* 并将静态页面上传到minio当中
* */
@Test
public void createStaticUrlTest() throws Exception {
//1查询文章表,获取文章内容
long apArticleId = 1383828014629179393L;
ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.lambdaQuery().eq(ApArticleContent::getArticleId,apArticleId));
//判断文章内容是否为空
if(apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())){
//2文章内容通过freemarker生成静态页面
//2.1获取模板
Template template = configuration.getTemplate("article.ftl");
//2.2准备数据 存储数据因为我们模板当中遍历,这时候字符串不行,要转换为对象
Map params = new HashMap<>();
params.put("content", JSONArray.parseArray(apArticleContent.getContent()));
//2.3输出流
StringWriter out = new StringWriter();
//2.4 合成,输出文件,
template.process(params,out);
//3把html文件上传到minio当中
//3.1将静态页面转化为字节输入流
InputStream is = new ByteArrayInputStream(out.toString().getBytes());
//3.2上传到minio,调用上传命令,前缀,名称,静态页面,返回的就是存储到minio当中静态页面访问地址
String path = fileStorageService.uploadHtmlFile("", apArticleContent.getArticleId() + ".html", is);
System.out.println("文件访问路径"+path);
//修改ap_article当中文章详情的路径
ApArticle apArticle = new ApArticle();
apArticle.setId(apArticleContent.getArticleId());
apArticle.setStaticUrl(path);
articleMapper.updateById(apArticle);
}
}
}
第一步: 再网关过滤器里过滤网址的时候,将用户信息存入header当中
第二步:网关通过后会将请求带入微服务,我们配置微服务的拦截器,将过来的所有i请求拦截,提前header当中的用户信息,使用工具类存入当前线程当中(工具类包括存,取,清除)等。
第三步:业务代码,需要注意的是 MultipartFile对象里面封装的是前端提供的图片存储位置,
比如查询图片素材
再serviceimpl里面的话直接调用page方法就是分页查询,然后创建的LambdaQueryWrapper对象里面可以添加其他查询条件。
3、文章管理(修改,保存)
请求参数形式
(依赖是skd-code,sdk-green)
需要使用的时候直接指定aliyun的keyid和secret就可以调用接口使用,文本审核直接传入文章内容,图片审核则是传入list集合的字节数组,即图片的字节数组。
(分布式id--雪花算法是mybatis-plus集成的,使用方法也比较方便)
第一:在实体类中的id上加入如下配置,指定类型为id_worker
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;第二:在application.yml文件中配置数据中心id和机器id
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml 指定映射文件位置
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.article.pojos
global-config:
datacenter-id: 1 datacenter-id:数据中心id(取值范围:0-31)
workerId: 1 workerId:机器id(取值范围:0-31)第三:具体实现
后面使用mybatispuls完成新增操作的时候自动生成的主键就是根据雪花算法而来
第一步::在fegin接口类重 编写降级逻辑(实现的是字节写的feign接口)
/**
* feign失败配置
* @author itheima
*/
@Component
public class IArticleClientFallback implements IArticleClient {
@Override
public ResponseResult saveArticle(ArticleDto dto) {
return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"获取数据失败");
}
}
第二步::在feign接口中,将降级处理类添加进去(fallback指定)
@FeignClient(value = "leadnews-article",fallback = IArticleClientFallback.class) //fallback指定feign调用失败的处理
public interface IArticleClient {
//文章保存或者修改 自媒体端保存后app端要将文章内容保存起来
@PostMapping("/api/v1/article/save")
public ResponseResult saveArticle(@RequestBody ArticleDto dto);
}
第三步::哪里需要调用feign接口,就在那个服务下配置
feign:
# 开启feign对hystrix熔断降级的支持
hystrix:
enabled: true
# 修改调用超时时间
client:
config:
default:
connectTimeout: 2000
readTimeout: 2000
第四步:: 因为新的服务扫描不到我们的feign服务降级处理类,所以我们需要在调用者下创建一个配置文件,扫描降级服务处理类
@Configuration
@ComponentScan("com.heima.apis.article.fallback")
public class InitConfig {
}
方案 说明
数据库模糊查询 效率太低 String.indexOf("")查找 数据库量大的话也是比较慢 全文检索 分词再匹配 DFA算法 确定有穷自动机(一种数据结构)
这个项目使用的是DFA算法,没有使用es分词器进行敏感词管理 (黑马头条中将DFA算法放在utils的SensitiveWordUtil中,以后遇到也可以直接拷贝使用)
DFA算法的实现原理
方案 说明 百度OCR 收费 Tesseract-OCR Google维护的开源OCR引擎,支持Java,Python等语言调用 Tess4J 封装了Tesseract-OCR ,支持Java调用
这里我们使用的是tess4j。
第一步:: 导入依赖
net.sourceforge.tess4j
tess4j
4.1.1
第二步: 创建Tesseract对象去完成图片文字识别
本项目使用的是我们的redis的zset和list来完成,list存储立即完成的,而zset存储我们的定时完成的内容。
给我们的sql任务表上锁,这里使用的是乐观锁
第一:;mybatis-plus支持数据库乐观锁的使用,所以在乐观锁哪里添加@Version注解
/**
* 版本号,用乐观锁
*/
@Version
private Integer version;第二步::添加乐观锁的支持,添加乐观锁的拦截器
/**
* mybatis-plus乐观锁支持
* @return
*/
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
当zest当中的source小于当前时间的值,将zest中的集合转到list集合当中(这里设定的是每分钟执行一次)
第二步:在Scheduled服务当中添加@EnableScheduling 启动定时任务注解
redis的分布式锁是通过sexnx的特效来完成分布式锁的作用。(在CacheService 工具类下面有一个tryLock方法,就是完成加锁的方法,传递锁名称,过期时间,得到一个token,可以通过判断token是不是为空,来实现加锁功能)
4、给文章发布添加延迟任务。
总的来说就是文章发布后,将文章添加到延迟队列任务当中,每秒中到redis的list集合当中拉取一次任务,
特性 ActiveMQ RabbitMQ RocketMQ Kafka 开发语言 java erlang java scala 单机吞吐量 万级 万级 10万级 100万级 时效性 ms us ms ms级以内 可用性 高(主从) 高(主从) 非常高(分布式) 非常高(分布式) 功能特性 成熟的产品、较全的文档、各种协议支持好 并发能力强、性能好、延迟低 MQ功能比较完善,扩展性佳 只支持主要的MQ功能,主要应用于大数据领域 消息中间件对比-选择建议
消息中间件 建议 Kafka 追求高吞吐量,适合产生大量数据的互联网服务的数据收集业务 RocketMQ 可靠性要求很高的金融互联网领域,稳定性高,经历了多次阿里双11考验 RabbitMQ 性能较好,社区活跃度高,数据量没有那么大,优先选择功能比较完备的RabbitMQ
produces消息的生产者,consumers消息的消费者,中间的kafka Cluster集群设置。
因为Kafka对于zookeeper是强依赖,保存kafka相关的节点数据,所以安装Kafka之前必须先安 装zookeeper
Docker安装zookeeper
下载镜像:
docker pull zookeeper:3.4.14创建容器
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.4.14
Docker安装kafka
下载镜像:
docker pull wurstmeister/kafka:2.12-2.3.1创建容器
docker run -d --name kafka \ --env KAFKA_ADVERTISED_HOST_NAME=192.168.200.128 \ 虚拟机服务器地址 --env KAFKA_ZOOKEEPER_CONNECT=192.168.200.128:2181 \ zookeeper连接地址 --env KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.200.128:9092 \ 对外的监听 --env KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \ --env KAFKA_HEAP_OPTS="-Xmx256M -Xms256M" \ --net=host wurstmeister/kafka:2.12-2.3.1 使用--net=host直接使用容器宿主机的命名空间,使用宿主机的ip和端口
第一步:导入依赖
org.apache.kafka
kafka-clients
第二步::生产者
这里注解,同步异步方法发送消息也在里面
/*生产者*/
public class ProducerQuickStart {
/*kafka入门案例*/
public static void main(String[] args) throws Exception {
//1.kafka配置
Properties prop = new Properties();
//连接地址
prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.200.128:9092");
//ack配置 消息确认机制
prop.put(ProducerConfig.ACKS_CONFIG,"1");
//发送失败,重新连接次数
prop.put(ProducerConfig.RETRIES_CONFIG,5);
//指定key,和value的序列化器,都是用的kafka包下的StringSerializer序列化器
prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
//创建kafka生产对象
KafkaProducer producer = new KafkaProducer(prop);
String str = "许石豪真帅";
//封装需要发送的消息(指定topic,key,value)
ProducerRecord record = new ProducerRecord("topic_first", "key_one", str);
//发送消息
/* 1.普通发送,直接send
producer.send(record);*/
/* 2.同步发送
RecordMetadata recordMetadata = producer.send(record).get();
System.out.println("发送消息成功,偏移量::"+recordMetadata.offset());*/
//3.异步发送
producer.send(record, new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
System.out.println("发送消息成功,偏移量::"+recordMetadata.offset());
if(e!=null){
System.out.println("一般出现异常的话会打印异常信息到日志当中,方便后续处理");
}
}
});
//关闭通道,不关闭的化消息会发送不成功
producer.close();
}
}
第三步:消费者
注意:其中的定义组,单个组有多个消费者的话只会发送消息给一个消费者,多个组,每个组下还有一个消费者,则会将消息发送给每个消费者
/*消费者*/
public class ConsumerQuickStart {
public static void main(String[] args) {
Properties prop = new Properties();
prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.200.128:9092");
//定义组,同个消费者组的话只会有一个消费者接收到消息,不同组的话就是多个组都接收到消息(所以单个组一对一,多个组,一对多)
prop.put(ConsumerConfig.GROUP_ID_CONFIG,"group2");
//设置key和value的反序列化器
prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
//创建消费者对象
KafkaConsumer kafkaConsumer = new KafkaConsumer(prop);
//订阅主题(topic)
kafkaConsumer.subscribe(Collections.singletonList("topic_first"));
//当前线程一直处于监听状态
while (true){
ConsumerRecords consumerRecords = kafkaConsumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord consumerRecord : consumerRecords) {
String key = consumerRecord.key();
String value = consumerRecord.value();
System.out.println(key+":::"+value);
}
}
}
}
4、参数详解
//ack配置 消息确认机制(all父节点和ISR节点还有普通节点都受到消息后,生产者才会收到消息发送成功的响应,一般我们都是使用的默认值)
prop.put(ProducerConfig.ACKS_CONFIG,"all");
确认机制 说明 acks=0 生产者在成功写入消息之前不会等待任何来自服务器的响应,消息有丢失的风险,但是速度最快 acks=1(默认值) 只要集群首领节点收到消息,生产者就会收到一个来自服务器的成功响应 acks=all 只有当所有参与赋值的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应
retries(重试次数,当发送消息失败,我们尝试几次发送)
//重试次数
prop.put(ProducerConfig.RETRIES_CONFIG,10);
//数据压缩
prop.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"lz4");
压缩算法 说明 snappy 占用较少的 CPU, 却能提供较好的性能和相当可观的压缩比, 如果看重性能和网络带宽,建议采用 lz4 占用较少的 CPU, 压缩和解压缩速度较快,压缩比也很客观 gzip 占用较多的 CPU,但会提供更高的压缩比,网络带宽有限,可以使用这种算法
自动提交错误::1.如果提交偏移量小于客户端处理的最后一个消息的偏移量,那么处于两个偏移量之间的消息就会被重复处理。
2.如果提交的偏移量大于客户端的最后一个消息的偏移量,那么处于两个偏移量之间的消息将会丢失。
所以我们可以采用手动提交的方式,记录偏移量(在消费者创建对象的时候,将自动提交管不
/设置偏移量手动提交
prop.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);)
(1)同步手动提交,
缺点:当发送的消息过多时,发起提交调用时应用会阻塞
优点:处理完所有记录后调用commitSync提交偏移量,解决了偏移量可能错误
while (true){
ConsumerRecordsrecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecordrecord : records) {
System.out.println(record.value());
System.out.println(record.key());
try {
consumer.commitSync();//同步提交当前最新的偏移量
}catch (CommitFailedException e){
System.out.println("记录提交失败的异常:"+e);
}}
} //这里可以对照前面的消费者看,进行了那些变化,都是在消息处理当中进行提交
(2)异步手动提交
可以很好的解决堵塞的现象
while (true){
ConsumerRecordsrecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecordrecord : records) {
System.out.println(record.value());
System.out.println(record.key());
}
consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Mapmap, Exception e) {
if(e!=null){
System.out.println("记录错误的提交偏移量:"+ map+",异常信息"+e);
}
}
});
}
(3)同步异步共同提交
如果服务器返回提交失败,异步提交不会进行重试,所以我们才需要同步异步共同提交的方式
try {
while (true){
ConsumerRecordsrecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecordrecord : records) {
System.out.println(record.value());
System.out.println(record.key());
}
consumer.commitAsync(); //异步
}
}catch (Exception e){+
e.printStackTrace();
System.out.println("记录错误信息:"+e);
}finally {
try {
consumer.commitSync(); //同步
}finally {
consumer.close();
}
}
第一步: 依赖配置
org.springframework.boot
spring-boot-starter-web
org.springframework.kafka
spring-kafka
org.apache.kafka
kafka-clients
org.apache.kafka
kafka-clients
com.alibaba
fastjson
第二步:yml配置
spring:
kafka:
bootstrap-servers: 192.168.200.128:9092
producer: (retries重试次数,key-serializer 指定序列化器)
retries: 5
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer: (group-id:消费组的id,后面两个:反序列化器的指定)
group-id: group-test
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
第三步:发送消息
@RestController
public class HelloController {
@Autowired
private KafkaTemplate kafkaTemplate;
@GetMapping("/hello")
public String hello(){
kafkaTemplate.send("itcast-topic","许石豪真帅");
return "ok";
}
}
第四步:设置监听器接收消息
@Component
public class HelloListener {
@KafkaListener(topics = "itcast-topic")
public void onMessage(String message){
if(!StringUtils.isEmpty(message)){
System.out.println(message);
}
}
}
1、使用的是es搜索引擎来完成搜索案例的实现。
新增文章后需要增加es当中索引文档,所以文章审核完成,在生成静态页面详情上传minio当中时发送信息到kafka,在搜索微服务当中接收消息调用es,完成文档索引的构建。
3、app搜索记录
(1)springboot整合mongodb
使用的是mongodb数据库,用户的搜索记录,需要给每一个用户都保存一份,数据量较大,要求加载速度快,通常这样的数据存储到mongodb更合适,不建议直接存储到关系型数据库中,因此选择mongodb
依赖
org.springframework.boot
spring-boot-starter-data-mongodb
yml数据配置
spring:
data:
mongodb:
host: 192.168.200.130
port: 27017
database: leadnews-history
3.。。pojo类(需要注意的是,@Document指定的是mongodb集合的名称,我们的主键id一般都是使用的string类型,因为会自动生成比较长的一串)
/**
*
* 联想词表
*
*
* @author itheima
*/
@Data
@Document("ap_associate_words")
public class ApAssociateWords implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
/**
* 联想词
*/
private String associateWords;
/**
* 创建时间
*/
private Date createdTime;
}
4.测试(注意,mongodb每次操作都要指名所使用的pojo类,因为其中有指定集合名称)
@SpringBootTest(classes = MongoApplication.class)
@RunWith(SpringRunner.class)
public class MongoTest {
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void saveTest(){
for (int i = 0; i < 10; i++) {
ApAssociateWords apAssociateWords = new ApAssociateWords();
apAssociateWords.setAssociateWords("许石豪");
apAssociateWords.setCreatedTime(new Date());
mongoTemplate.save(apAssociateWords);
}
}
@Test
public void selectTest(){
ApAssociateWords byId = mongoTemplate.findById("654c74ee68e63d296a82926e", ApAssociateWords.class);
System.out.println(byId);
}
//按条件查询
@Test
public void testQuery(){
Query query =Query.query(Criteria.where("associateWords").is("许石豪"))
.with(Sort.by(Sort.Direction.DESC,"createdTime"));
List apAssociateWords = mongoTemplate.find(query, ApAssociateWords.class);
System.out.println(apAssociateWords);
}
@Test
public void deleteTest(){
Query query = Query.query(Criteria.where("associateWords").is("许石豪"));
mongoTemplate.remove(query, ApAssociateWords.class);
}
}
(2)搜索记录的保存