lombok
功能
- 通过注解的方式在定义类的时候减少样板代码,并可以自动化生成日志变量
使用
@Getter/@Setter
:为所有的属性提供get/set方法@ToString
:会给类自动生成易阅读的toString方法@EqualsAndHashCode
:根据类所拥有的非静态字段自动重写equals方法和hashCode方法@Data
:提供了更综合的生成代码功能(@Getter+@Setter+@ToString+@EqualsAndHashCode)@NoArgsConstructor
:为实体类生成无参的构造器方法@AllArgsConstructor
:为实体类生成除了static修饰的字段之外带有各参数的构造器方法
mybatis-plus
功能
- 进行了增强的 ORM 框架(对象关系映射),提供了 自动映射 功能,不再需要写
resultMap
- 内置了
BaseMapper
接口,提供了很多常用的 CRUD 操作,支持自动生成 SQL 语句 - 内置了分页功能
- 内置了逻辑删除功能(通过在数据库表中添加一个表示删除状态的字段,而不是直接从数据库中删除记录)
- 提供了自动填充功能(创建时间、更新时间…)
- 提供了性能分析插件
- 提供了代码生成器,可以根据数据库表自动生成实体类、Mapper、Service 等代码
使用
- 添加依赖
<dependencies>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version> <!-- 请根据实际需要选择版本 -->
</dependency>
<!-- MyBatis 和 Spring Boot Starter 依赖(如果没有的话)-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
- 配置yml文件
mybatis-plus:
type-aliases-package: com.example.demo.entity # 实体类包路径
- 创建实体类
@TableName
:指定实体类映射的数据库表名@TableId
:指定实体类的主键字段 (如果没有该注解,MyBatis-Plus 会默认认为id
字段是主键)value
:指定主键对应的字段名type
:指定主键策略IdType.AUTO
:自增主键(MySQL)IdType.INPUT
:用户输入主键(如 UUID)IdType.ID_WORKER
:雪花算法生成主键(适合分布式系统)
@TableField
:指定实体类字段与数据库表列的映射关系value
:指定数据库表字段名(默认为字段名)exist
:是否是数据库表中的字段,默认为true
,如果该字段不映射到数据库,则设置为false
。fill
:设置字段的自动填充策略 (需要实现MetaObjectHandler接口)FieldFill.DEFAULT
:默认不处理,即不进行自动填充FieldFill.INSERT
:插入时自动填充字段FieldFill.UPDATE
:更新时自动填充字段FieldFill.INSERT_UPDATE
:插入和更新时都自动填充字段
update
:指定更新时字段的策略select
:指定是否需要在查询时被映射condition
:指定字段在 SQL 语句中的条件SqlCondition.LIKE
:使用 SQL 中的 LIKE 条件进行模糊查询SqlCondition.EQ
:默认,表示使用等于条件- 自定义字符串:可以提供一个自定义的字符串条件
@TableLogic
:用于指定实体类的逻辑删除字段,支持自动填充逻辑删除值@Version
:更新时检查数据版本,如果检测到数据版本在读取后已经被其他事务修改,当前事务将拒绝更新,从而防止数据不一致
@TableName("user") // 表示该实体类映射到数据库中的 "user" 表
public class User {
@TableId(value = "id", type = IdType.AUTO) // 主键自增
private Long id;
@TableField(value = "user_name", fill = FieldFill.INSERT,condition = SqlCondition.LIKE) // 插入时自动填充
private String name;
@Version // 乐观锁版本字段
private Integer age;
@TableLogic // 逻辑删除字段
private Integer deleted;
@TableField(exist = false) // 表示该字段不对应数据库表中的字段
private String tempField;
@TableField(select = false) // 在查询时不查询该字段
private String password;
}
- 创建Mapper接口
BaseMapper
:继承基本接口,并传入实体类,获得该类常用的CRUD基本方法- insert:插入一条记录
int insert(T entity)
- insertOrUpdate:根据实体类的主键值判断,如果主键存在则执行更新操作,如果主键不存在则执行插入操作
boolean insertOrUpdate(T entity)
- updateById:根据主键 ID 更新相应实体类的所有字段
int updateById(T entity)
- update:根据条件更新记录
int update(T entity, Wrapper<T> updateWrapper)
- selectById:根据主键 ID 查询单条记录
T selectById(Serializable id)
- selectBatchIds:根据 ID 列表查询多条记录
List<T> selectBatchIds(Collection<? extends Serializable> idList)
- selectOne:根据条件查询单条记录。如果查询结果有多条,抛出异常
T selectOne(Wrapper<T> queryWrapper)
- selectList:根据条件查询多条记录
List<T> selectList(Wrapper<T> queryWrapper)
- selectPage:进行分页查询
IPage<T> selectPage(Page<T> page, Wrapper<T> queryWrapper)
- delete:根据条件删除记录
int delete(Wrapper<T> queryWrapper)
- deleteById:根据主键 ID 删除一条记录
int deleteById(Serializable id)
- deleteBatchIds:
int deleteBatchIds(Collection<? extends Serializable> idList)
- selectCount:根据条件查询记录的总数
int selectCount(Wrapper<T> queryWrapper)
- exists:判断是否存在符合条件的记录
boolean exists(Wrapper<T> queryWrapper)
- insert:插入一条记录
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,自动获得 CRUD 操作方法
// 不需要编写基本的插入、删除、更新、查询方法
}
- CRUD
- 查询
- 条件查询
- 构造条件查询对象
- QueryWrapper:构建查询条件,用于生成 WHERE 子句
QueryWrapper<MyEntity> queryWrapper = new QueryWrapper<>()
- LambdaQueryWrapper:使用 Lambda 表达式构建查询条件
LambdaQueryWrapper<MyEntity> lambdaQuery = new LambdaQueryWrapper<>()
- QueryWrapper:构建查询条件,用于生成 WHERE 子句
- 添加查询条件(对象方法)
eq
(equals):等于查询条件ne
(not equals):不等于查询条件gt
(greater than):大于查询条件ge
(greater than or equals to):大于或等于查询条件lt
(less than):小于查询条件le
(less than or equals to):小于或等于查询条件like
:模糊查询条件,可以包含通配符 %likeLeft
:左模糊查询条件,通配符 % 在字符串的左侧likeRight
:右模糊查询条件,通配符 % 在字符串的右侧in
:在给定列表中的查询条件notIn
:不在给定列表中的查询条件orderByAsc
:升序排序orderByDesc
:降序排序groupBy
:分组查询select
:指定查询的字段distinct
:去重查询nested
:嵌套查询条件or
:逻辑或and
:逻辑与
/** * 查找price>30000并且star=4的记录或者price<1000的记录,记录只显示id、name、price、star字段 */ @Test public void testSelectByPriceAndStar() { QueryWrapper<Goods> queryWrapper = new QueryWrapper<>(); queryWrapper.select("id", "name", "price", "star") .and(i -> i.gt("price", 30000).eq("star", 4)) .or(i -> i.lt("price", 1000)); List<Goods> goods = goodsMapper.selectList(queryWrapper); goods.forEach(System.out::println); System.out.println("测试成功"); }
- 构造条件查询对象
- 分页查询
- 配置分页查询插件:通过
PaginationInterceptor
插件拦截所有的查询语句,在其上添加分页逻辑
@Configuration @EnableTransactionManagement public class MybatisPlusConfig{ @Bean // Spring容器会调用这个方法,并把返回值当作一个bean的实例 public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 创建拦截器 PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); // 创建分页插件 paginationInnerInterceptor.setOptimizeJoin(true); // 设置分页插件的联表查询优化 paginationInnerInterceptor.setDbType(DbType.MYSQL); // 设置数据库类型为MySQL paginationInnerInterceptor.setOverflow(true); // 设置分页插件的物理分页参数溢出时的处理策略 interceptor.addInnerInterceptor(paginationInnerInterceptor); // 将分页插件添加到拦截器 OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor(); //创建乐观锁插件 interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor); // 将乐观锁插件添加到拦截器 return interceptor; // 返回拦截器 }
- 配置分页查询插件:通过
- 条件查询
}
2. 创建分页对象:`Page` 类表示分页的请求参数(如当前页和每页的记录数),`IPage` 是分页查询返回的结果。 - `Page<MyEntity> page = new Page<>(pageNo, pageSize)` - `IPage<User> userPage = userMapper.selectPage(page, queryWrapper);`
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public IPage<User> getUsersByPage(int pageNo, int pageSize) {
// 创建分页对象
Page<User> page = new Page<>(pageNo, pageSize); // 当前页和每页的记录数
// 创建查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", "active"); // 查询条件:status = 'active'
// 执行分页查询,返回分页查询结果对象
IPage<User> userPage = userMapper.selectPage(page, queryWrapper);
return userPage;
}
}
```
3. 分析返回结果:`IPage` 接口提供了很多分页信息
- `getRecords()`:获取当前页的数据(一个 `List`,包含查询的实体类对象)
- `getTotal()`:获取总记录数
- `getPages()`:获取总页数
- `getCurrent()`:获取当前页码
- `getSize()`:获取每页的记录数
```java
@Controller
public class UserController {
@Autowired
private UserService userService;
public void getUsers() {
// 调用查询方法,返回分页结果
IPage<User> userPage = userService.getUsersByPage(1, 10);
// 获取分页数据
List<User> users = userPage.getRecords(); // 获取当前页的数据
long total = userPage.getTotal(); // 总记录数
long totalPages = userPage.getPages(); // 总页数
long currentPage = userPage.getCurrent(); // 当前页码
long pageSize = userPage.getSize(); // 每页记录数
// 处理分页数据...
}
}
```
细节
- 下划线转驼峰,默认情况下mybatis-plus是开启的,而mybatis是关闭的
mybatis-flex
功能
- 相比于mybatis-plus,性能更强,功能更全面
使用
- @Table
- @Id
JWT令牌
功能
- 使用JSON Web Token进行身份认证,进行访问控制
- 解决 Web 应用中的无状态认证问题,可以安全地传输用户身份信息和权限等数据
特点
- JWT是自包含的,服务器不需要存储会话信息,所有信息都在令牌中
构成
Header
:头部,通常包含令牌类型(如JWT
)和加密算法(如HS256
)。Payload
:有效负载,包含要传输的声明(如用户信息、权限等)。有效负载部分并不加密,敏感信息请避免放在这里。Signature
:签名,用于确保数据没有被篡改。由 Header 和 Payload 通过加密算法进行签名生成。
使用
- 引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
<!-- Spring Security(如果你需要进行安全控制) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 创建 JWT 工具类
public class JwtUtil {
// 密钥,生产环境中需要保密,注意不能过短,否则会引起异常
private static final String SECRET_KEY = "mysecretkey";
// 生成 JWT 令牌
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username) // 设置主题(通常是用户名)
.setIssuedAt(new Date()) // 设置签发时间
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 设置过期时间(1小时)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 使用 HMAC SHA256 算法进行签名
.compact(); // 将令牌转为字符串
}
// 解析 JWT 令牌
public static Claims extractClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
// 从 JWT 令牌中获取用户名(或者其他信息)
public static String extractUsername(String token) {
return extractClaims(token).getSubject();
}
// 检查 JWT 是否过期
public static boolean isTokenExpired(String token) {
return extractClaims(token).getExpiration().before(new Date());
}
// 验证 JWT 令牌
public static boolean validateToken(String token, String username) {
return (username.equals(extractUsername(token)) && !isTokenExpired(token));
}
}
- 创建过滤器:进行拦截请求并验证JWT
- 流程:拦截->处理->放行->返回
@WebFilter("/*") // 配置拦截资源的路径,该配置是拦截所有请求
public class JwtFilter implements Filter {
@Override // 拦截请求后调用,调用多次
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 将ServletRequest类型转换为HttpServletRequest,这样可以访问HTTP请求特有的方法和属性
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 从请求头中获取 JWT
String token = httpRequest.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // 去掉 "Bearer " 前缀
String username = JwtUtil.extractUsername(token); // 从token中提取用户名
if (username != null && JwtUtil.validateToken(token, username)) {
// 如果token验证通过,将用户名设置到请求属性中,这样后续的代码可以通过请求对象获取到用户名。
httpRequest.setAttribute("username", username);
}
}
// 放行请求(调用FilterChain的doFilter方法,将请求传递给过滤器链中的下一个过滤器或目标资源)
chain.doFilter(request, response);
// 资源访问后处理逻辑...
}
@Override // 初始化方法,只调用一次,用默认实现方法,可以不实现
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override // //销毁方法,只调用一次,用默认实现方法,可以不实现
public void destroy() {
}
}
- 注册过滤器:对创建好的过滤器进行基本配置,添加到Spring容器中
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JwtFilter> loggingFilter() {
FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*"); // 配置哪些接口需要进行 JWT 认证
return registrationBean;
}
}
Last updated on