Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式

Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤

经过我三天三夜折磨,搞好了这个需求。
首先这个需求一般是通过注解的方式来实现。

所以第一步手写一个注解:

@Target({ElementType.PARAMETER, ElementType.METHOD})//注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface DataFilter {
}

第二步,在自己的实体类上面添加一些东西

package com.dapeng.multitentant.entity;

import com.dapeng.multitentant.common.AbstractBaseEntity;
import lombok.Data;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.ParamDef;

import javax.persistence.*;


@Data
@Entity
@Table(name = "user_test")
@FilterDefs({
        @FilterDef(name="tenant",
        defaultCondition = "tenant_id = :tenantIdLimit",
        parameters = {@ParamDef(name = "tenantIdLimit", type = "string")}
        )
})
@Filter(name = "tenant", condition = "tenant_id = :tenantIdLimit")
public class Person extends AbstractBaseEntity {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

//    @TenantId // springboot 3.x支持分区
    @Column(name = "tenant_id")
//    @Filter(name = "tenant", condition  = "tenant_id >= (:tenantIdLimit)")
    private String tenantId;

    @PrePersist
    protected void prePersist(){
        System.out.println(tenantId);
    }

    @Column(name = "name")
    private String name;

    @Column(name = "age")
    private Integer age;
}

第三步,手写切面类(注意@Aspect和@Component),并写上切入点。

package com.dapeng.multitentant.AOP;

import com.dapeng.multitentant.annonation.DataFilter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;


@Aspect
@Component
public class TenantAop {
    //EntityManager是JPA中用于增删改查的接口,它的作用相当于一座桥梁,连接内存中的java对象和数据库的数据存储。
    //1
    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    @Before("@annotation(dataFilter)")
    public void doProcess(JoinPoint joinPoint, DataFilter dataFilter) throws Throwable {
        System.out.println("进入切面了~~~~~~~~~~~");
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request= (HttpServletRequest) RequestContextHolder.getRequestAttributes().resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Map<String, String> rtnMap = converMap(request.getParameterMap());
        String tenantId = rtnMap.get("tenantId");
        if(!StringUtils.isEmpty(tenantId)){
	 		 // 固定写法
	        // 2
	        Session session = entityManager.unwrap(Session.class);
	        // 自定义的属性名
	        // 3
	        Filter filter = session.enableFilter("tenant");
	        //自定义属性值名,设置值,该值可以是集合类型,可以是基本类型,这里是集合类型
	        filter.setParameter("tenantIdLimit", tenantId);
		}
      
    }
}

第四步,直接在Controller里面调用自己的方法吧。

package com.dapeng.multitentant.controller;

import com.dapeng.multitentant.annonation.DataFilter;
import com.dapeng.multitentant.entity.Person;

import com.dapeng.multitentant.repository.PersonRepository;

import com.dapeng.multitentant.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.transaction.Transactional;
import java.util.List;

@RestController
@RequestMapping("/people")
public class PersonController {
    @Autowired
    PersonService service;
    @Autowired
    PersonRepository repository;

    @GetMapping("/list")
    @DataFilter
    List<Person> all(Person person){
        List<Person> byName = repository.findByName(person.getName());
        return byName;
    }
}

看下面的效果图:

可以看到Hibernate自动帮我们把tenant_id条件加入上去了,的确很方便。不过配置的时候花了我三天时间去搜索相关资料。
在这里插入图片描述

Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式_第1张图片
1.查询租户ID为1的数据:
Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式_第2张图片
2.查询租户ID为1中不存在的数据,并且名字跟别的租户姓名一样。
Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式_第3张图片

好了,感谢大家关注,如果上面有错误或者不合适的地方,还请大家多多提醒。感谢~

修改:

2023.9.15日,遇到的问题:

使用注解的时候,发现SQL语句里面并没有添加相对应的过滤条件。
原因可能是:
1.没有使用hql。得使用Repository接口里面的方法。
	例如:(这里我注释掉了,应该用框框里面的代码,还有自定义的注解@DataFilter)

Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式_第4张图片

2.在AOP切点的方法上面添加@Transactional(必须的),使用@Around来描述。然后参数有ProceedingJoinPoint。然后返回joinPoint.proceed()。

Hibernate + Springboot 实现@Filter过滤器,进行自动添加条件过滤。sprinboot多租户(共享表,通过添加tenant_id)实现方式_第5张图片

你可能感兴趣的:(hibernate,spring,boot,java)