PageHelper和TableSupport都是用于处理分页查询的工具类,但它们有一些区别。
1.底层实现:PageHelper是一个功能强大且广泛使用的分页查询插件,主要用于集成持久层框架(如MyBatis)实现分页查询。它通过拦截器的方式拦截SQL语句并自动进行分页查询。TableSupport,则是一个封装和处理分页查询参数的工具类,用于在Java Servlet开发中处理分页查询参数。
2.依赖关系:PageHelper作为一个分页查询插件,需要与持久层框架(如MyBatis)进行集成使用,需要在项目中添加对PageHelper的依赖。而TableSupport是一个通用的工具类,可以直接在Java Servlet开发中使用,不需要额外的依赖。
3.功能范围:PageHelper提供了更为丰富和灵活的分页查询功能,支持动态排序、多个分页查询、多层嵌套查询等。它还可以与其他高级查询功能(如条件查询、关联查询)配合使用。TableSupport则更加简化,主要用于封装和处理分页查询参数,提供了获取当前页码、每页显示数量、排序参数等功能。
4.应用场景:PageHelper适用于需要在持久层框架中进行高级分页查询的情况,尤其是与MyBatis框架集成时。TableSupport则适用于需要在Java Servlet开发中进行简单的分页查询,用于处理分页查询参数的传递和获取。
总体而言,PageHelper适用于复杂的分页查询需求,尤其是在集成持久层框架时;而TableSupport则适用于简单的分页查询需求,特别是在Java Servlet开发中使用。选择使用哪个工具类,取决于具体的项目需求和开发场景。
对于日志的自定义注解,通常需要自己编写切面,原因如下:
1.自定义注解只是标记,不具备切面逻辑:自定义注解本身只是一个标记,指示哪些方法需要记录日志,但它本身并不能实现切面逻辑,即在目标方法执行前后进行日志记录。因此,需要编写切面来定义日志记录的具体逻辑。
2.日志记录与业务逻辑相分离:切面的作用是实现与业务逻辑相分离的横切关注点,并提供一种通用的方式来处理这些关注点。日志记录通常不仅仅是简单的打印一行日志,还可能涉及日志级别、日志内容格式化等复杂操作,因此将日志记录逻辑放在切面中,能够使业务逻辑更加清晰和干净。
3.切面提供了更多的灵活性:编写切面类可以更加灵活地控制日志记录的行为,例如可以根据注解的参数灵活地指定日志级别、自定义日志格式、记录的内容等。通过切面,可以根据具体需求定制日志记录的方式,而不是局限于自定义注解的设计。
综上所述,为了实现日志记录的具体逻辑以及提供更大的灵活性,通常需要自己编写切面来配合日志的自定义注解使用。
对于自定义Excel的实现,通常情况下不需要编写切面逻辑,原因如下:
1.Excel导出逻辑相对简单:相比于其他横切关注点,如日志记录、异常处理等,Excel导出逻辑相对来说相对简单。一般而言,Excel导出只涉及数据的提取和格式化操作,并不需要像日志记录那样进行复杂的切面处理。
2.切面对Excel导出的影响较小:Excel导出通常是在业务方法执行结束后进行的,而且对业务方法本身并没有特别的要求。因此,大部分情况下不需要使用切面来控制Excel导出的行为。
3.自定义Excel注解主要用于元数据的配置:自定义Excel注解一般是用于标记属性和字段的元数据,通过注解完成Excel导出时的表头、列名、样式等属性的定义。这些注解在Excel导出时仅起到元数据配置的作用,不需要切面来实现逻辑。
当然,如果你有特定的需求,比如需要在Excel导出前后进行一些额外的处理,例如权限验证、数据校验等,则可以考虑编写切面来实现这些功能。但一般情况下,自定义Excel不需要复杂的切面逻辑来实现导出功能。
shiro
特性:认证 授权 会话管理 加密
@Transactional注解坑点
坑点1:Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。
@Transactional(rollbackFor = Exception.class)
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new SQLException("发生异常了..");
}
return rows;
}
坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new RuntimeException("发生异常了..");
}
return rows;
}
全局异常处理器
定义xx异常
public class CustomException extends RuntimeException{
public CustomException(String message){
super(message);
}
}
全局异常统一处理 @ControllerAdvice
annotations 标识哪些控制器需要进行全局异常处理
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalException {
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandeler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] s = ex.getMessage().split(" ");
String name = s[2]+"已存在";
return R.error(name);
}
return R.error("未知错误");
}
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandeler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}
使用
public void remove(Long id){
//查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常
LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper();
lambdaQueryWrapper.eq(Dish::getCategoryId,id);
int count = dishService.count(lambdaQueryWrapper);
if(count>0){
//抛出异常
throw new CustomException("当前分类下关联了菜品,不能删除");
}
}
数据校验使用
1.在controller上声明@Validated需要对数据进行校验。
public AjaxResult add(@Validated @RequestBody SysUser user)
{
.....
}
2.然后在对应字段Get方法加上参数校验注解,如果不符合验证要求,则会以message的信息为准,返回给前端。
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
public String getNickName()
{
return nickName;
}
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
public String getUserName()
{
return userName;
}
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
public String getEmail()
{
return email;
}
@Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
public String getPhonenumber()
{
return phonenumber;
}
也可以直接放在字段上面声明。
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
private String nickName;
自定义注解校验
使用原生的@Validated进行参数校验时,都是特定的注解去校验(例如字段长度、大小、不为空等),我们也可以用自定义的注解去进行校验,例如项目中的@Xss注解。
1、新增Xss注解,设置自定义校验器XssValidator.class
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Constraint(validatedBy = { XssValidator.class })
public @interface Xss
{
String message()
default "不允许任何脚本运行";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2、自定义Xss校验器,实现ConstraintValidator接口。
/**
* 自定义xss校验注解实现
*
* @author ruoyi
*/
public class XssValidator implements ConstraintValidator<Xss, String>
{
private final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
{
return !containsHtml(value);
}
public boolean containsHtml(String value)
{
Pattern pattern = Pattern.compile(HTML_PATTERN);
Matcher matcher = pattern.matcher(value);
return matcher.matches();
}
}
3、实体类使用自定义的@Xss注解
@Xss(message = "登录账号不能包含脚本字符")
@NotBlank(message = "登录账号不能为空")
@Size(min = 0, max = 30, message = "登录账号长度不能超过30个字符")
public String getLoginName()
{
return loginName;
}
自定义分组校验
有时候我们为了在使用实体类的情况下更好的区分出新增、修改和其他操作验证的不同,可以通过groups属性设置。使用方式如下
新增类接口,用于标识出不同的操作类型
public interface Add
{
}
public interface Edit
{
}
Controller.java
// 新增
public AjaxResult addSave(@Validated(Add.class) @RequestBody Xxxx xxxx)
{
return success(xxxx);
}
// 编辑
public AjaxResult editSave(@Validated(Edit.class) @RequestBody Xxxx xxxx)
{
return success(xxxx);
}
Model.java
// 仅在新增时验证
@NotNull(message = "不能为空", groups = {Add.class})
private String xxxx;
// 在新增和修改时验证
@NotBlank(message = "不能为空", groups = {Add.class, Edit.class})
private String xxxx;
提示
如果你有更多操作类型,也可以自定义类统一管理,使用方式就变成了Type.Add、Type.Edit、Type.Xxxx等。
package com.eva.core.constants;
/**
* 操作类型
*/
public interface Type
{
interface Add {}
interface Edit {}
interface Xxxx {}
}
数据权限使用
部门数据权限注解
@DataScope(deptAlias = "d")
public List<...> select(...)
{
return mapper.select(...);
}
部门及用户权限注解
@DataScope(deptAlias = "d", userAlias = "u")
public List<...> select(...)
{
return mapper.select(...);
}
3、在mybatis查询底部标签添加数据范围过滤
<select id="select" parameterType="..." resultMap="...Result">
<include refid="select...Vo"/>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
例如:用户管理(未过滤数据权限的情况):
select u.user_id, u.dept_id, u.login_name, u.user_name, u.email
, u.phonenumber, u.password, u.sex, u.avatar, u.salt
, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by
, u.create_time, u.remark, d.dept_name
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'
例如:用户管理(已过滤数据权限的情况):
select u.user_id, u.dept_id, u.login_name, u.user_name, u.email
, u.phonenumber, u.password, u.sex, u.avatar, u.salt
, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by
, u.create_time, u.remark, d.dept_name
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
where u.del_flag = '0'
and u.dept_id in (
select dept_id
from sys_role_dept
where role_id = 2
)
结果很明显,我们多了如下语句。通过角色部门表(sys_role_dept)完成了数据权限过滤
and u.dept_id in (
select dept_id
from sys_role_dept
where role_id = 2
)