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

本文详细介绍了如何在Springboot项目中,利用Hibernate和自定义注解,实现@Filter过滤器对多租户数据的自动条件过滤,包括创建注解、实体类的配置、AOP切面的编写以及Controller中的应用。
摘要由CSDN通过智能技术生成

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条件加入上去了,的确很方便。不过配置的时候花了我三天时间去搜索相关资料。
在这里插入图片描述

数据库中的一些数据
1.查询租户ID为1的数据:
在这里插入图片描述
2.查询租户ID为1中不存在的数据,并且名字跟别的租户姓名一样。
在这里插入图片描述

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

修改:

2023.9.15日,遇到的问题:

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

在这里插入图片描述

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

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现MySQL多租户数据隔离的另一种常见方法是通过在添加一个`tenant_id`列来标识不同的租户。下面是使用Java代码实现该方法的步骤: 1. 创建一个Java类来管理数据库连接和操作。这个类应该包含以下方法: ``` public class DatabaseManager { private static final String DATABASE_HOST = "localhost"; private static final String DATABASE_USERNAME = "root"; private static final String DATABASE_PASSWORD = "password"; private static final String DATABASE_NAME = "my_database"; private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; private Connection connection; public DatabaseManager() throws SQLException, ClassNotFoundException { Class.forName(DATABASE_DRIVER); this.connection = DriverManager.getConnection("jdbc:mysql://" + DATABASE_HOST + "/" + DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD); } public void executeQuery(String query) throws SQLException { Statement statement = connection.createStatement(); statement.executeQuery(query); statement.close(); } public void executeUpdate(String query) throws SQLException { Statement statement = connection.createStatement(); statement.executeUpdate(query); statement.close(); } public void closeConnection() throws SQLException { this.connection.close(); } } ``` 2. 创建一个Java类来管理租户列。这个类应该包含以下方法: ``` public class TenantManager { private Map<String, DatabaseManager> tenantDatabaseManagerMap = new HashMap<>(); public void addTenant(String tenantId) throws SQLException, ClassNotFoundException { if (!tenantDatabaseManagerMap.containsKey(tenantId)) { DatabaseManager databaseManager = new DatabaseManager(); databaseManager.executeUpdate("CREATE TABLE IF NOT EXISTS table_name (id INT PRIMARY KEY, column1 VARCHAR(255), column2 VARCHAR(255), tenant_id VARCHAR(36))"); tenantDatabaseManagerMap.put(tenantId, databaseManager); } } public DatabaseManager getDatabaseManager(String tenantId) { return tenantDatabaseManagerMap.get(tenantId); } public void closeAllConnections() throws SQLException { for (DatabaseManager databaseManager : tenantDatabaseManagerMap.values()) { databaseManager.closeConnection(); } tenantDatabaseManagerMap.clear(); } } ``` 3. 在每次请求到达时,从请求中获取租户ID,并使用TenantManager获取对应的DatabaseManager对象。然后使用该对象执行数据库操作。例如: ``` public class RequestHandler { private static final String TENANT_HEADER = "Tenant-Id"; private TenantManager tenantManager; public RequestHandler() { tenantManager = new TenantManager(); } public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, SQLException, ClassNotFoundException { String tenantId = request.getHeader(TENANT_HEADER); if (tenantId == null) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.getWriter().println("Tenant Id is required."); return; } DatabaseManager databaseManager = tenantManager.getDatabaseManager(tenantId); if (databaseManager == null) { tenantManager.addTenant(tenantId); databaseManager = tenantManager.getDatabaseManager(tenantId); } String insertQuery = "INSERT INTO table_name (id, column1, column2, tenant_id) VALUES (1, 'value1', 'value2', '" + tenantId + "')"; databaseManager.executeUpdate(insertQuery); String selectQuery = "SELECT * FROM table_name WHERE tenant_id = '" + tenantId + "'"; ResultSet resultSet = databaseManager.executeQuery(selectQuery); while (resultSet.next()) { int id = resultSet.getInt("id"); String column1 = resultSet.getString("column1"); String column2 = resultSet.getString("column2"); // process results } resultSet.close(); } public void destroy() throws SQLException { tenantManager.closeAllConnections(); } } ``` 以上就是使用Java代码实现MySQL通过添加`tenant_id`列来标识不同租户的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱折磨键盘的大鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值