# boot-demo
**Repository Path**: liang-tian-yu/boot-demo
## Basic Information
- **Project Name**: boot-demo
- **Description**: 记录SpringBoot的demo用例
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-01-04
- **Last Updated**: 2025-09-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## boot-demo
Spring Boot案例
记录SpringBoot的demo用例
[TOC]
## JPA
- 导入依赖
```
org.springframework.boot
spring-boot-starter-data-jpa
```
- yml配置
```
spring:
jpa:
hibernate:
ddl-auto: update
naming:
# 驼峰命名
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
# 默认引擎为InnoDB
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
```
- **实体类**
@Entity // 作为 hibernate实体类
@Table(name = "tb_name") // 配置数据库表的名称,实体类中属性和表中字段的映射关系
- 具体测试看JpaTest
## Knife4j
接口文档
配置详见`Knife4jConfig`
application.yml
```
# 解决swagger和springBoot高版本冲突问题
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
```
## MybaisPlus
自定义生成主键策略
- 定义主键策略
```
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Long nextId(Object entity) {
String serialId = SerialUtil.generateSerial();
return Long.valueOf(serialId);
}
}
```
- 注入
```
@Configuration
@MapperScan({"com.lty.mapper","com.lty.*.mapper"})
public class MybatisPlusConfig {
//@Bean
//public IdentifierGenerator identifierGenerator() {
// return new CustomIdGenerator();
//}
}
```
- 注解使用
```
@TableId(type = IdType.ASSIGN_ID, value = "id")
private String id;
```
[油猴脚本](https://juejin.cn/post/7517081861975277603)
- websocket
- spring-security
- boot-test
- antdesign tree树使用
## TreeUtil
```plain
package com.lty.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @Description: 树操作方法工具类
* @author lty
*/
public class TreeUtil {
/**
* 使用Map合成树
*
* @param menuList 需要合成树的List
* @param pId 对象中的父ID字段,如:Menu:getPid
* @param id 对象中的id字段 ,如:Menu:getId
* @param rootCheck 判断E中为根节点的条件,如:x->x.getPId()==-1L , x->x.getParentId()==null,x->x.getParentMenuId()==0
* @param setSubChildren E中设置下级数据方法,如: Menu::setSubMenus
* @param ID字段类型
* @param 泛型实体对象
* @return
*/
public static List makeTree(List menuList, Function pId, Function id, Predicate rootCheck, BiConsumer> setSubChildren) {
// 按原数组顺序构建父级数据Map,使用Optional考虑pId为null
Map, List> parentMenuMap = menuList.stream().collect(Collectors.groupingBy(
node -> Optional.ofNullable(pId.apply(node)),
LinkedHashMap::new,
Collectors.toList()
));
List result = new ArrayList<>();
for (E node : menuList) {
// 添加到下级数据中
setSubChildren.accept(node, parentMenuMap.get(Optional.ofNullable(id.apply(node))));
// 如里是根节点,加入结构
if (rootCheck.test(node)) {
result.add(node);
}
}
return result;
}
/**
* 树中过滤
*
* @param tree 需要过滤的树
* @param predicate 过滤条件
* @param getChildren 获取下级数据方法,如:MenuVo::getSubMenus
* @param 泛型实体对象
* @return List 过滤后的树
*/
public static List filter(List tree, Predicate predicate, Function> getChildren) {
return tree.stream().filter(item -> {
if (predicate.test(item)) {
List children = getChildren.apply(item);
if (children != null && !children.isEmpty()) {
filter(children, predicate, getChildren);
}
return true;
}
return false;
}).collect(Collectors.toList());
}
/**
* 树中搜索
*
* @param tree
* @param predicate
* @param getSubChildren
* @param
* @return 返回搜索到的节点及其父级到根节点
*/
public static List search(List tree, Predicate predicate, Function> getSubChildren) {
Iterator iterator = tree.iterator();
while (iterator.hasNext()) {
E item = iterator.next();
List childList = getSubChildren.apply(item);
if (childList != null && !childList.isEmpty()) {
search(childList, predicate, getSubChildren);
}
if (!predicate.test(item) && (childList == null || childList.isEmpty())) {
iterator.remove();
}
}
return tree;
}
}
```
## websocket
### 后端
- 导入依赖
```plain
org.springframework.boot
spring-boot-starter-websocket
```
- 配置
```plain
package com.lty.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws")
.setAllowedOrigins("*");
}
}
```
- 处理器
```plain
package com.lty.websocket;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class MyWebSocketHandler extends TextWebSocketHandler {
private static final Set sessions =
Collections.synchronizedSet(new HashSet<>());
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
log("新连接: " + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
String payload = message.getPayload();
log("收到消息: " + payload);
// 广播消息
sessions.forEach(s -> {
if (s.isOpen() && !s.equals(session)) {
try {
s.sendMessage(new TextMessage("广播: " + payload));
} catch (Exception e) {
log("发送消息失败: " + e.getMessage());
}
}
});
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
log("连接关闭: " + session.getId());
}
private void log(String message) {
System.out.println("[MyWebSocketHandler] " + message);
}
}
```
注意websocket地址是ws://localhost:8088/api/ws (后端地址+上下文路径+后缀)
### 前端
```plain
```