Heim >Java >javaLernprogramm >So erstellen Sie ein mandantenfähiges Springboot-SaaS

So erstellen Sie ein mandantenfähiges Springboot-SaaS

WBOY
WBOYnach vorne
2023-05-12 16:49:061661Durchsuche

Technisches Framework

Springboot-Version ist 2.3.4.RELEASE

Die Persistenzschicht übernimmt JPA

Mandantenmodelldesign

Da alle Mandanten der Saas-Anwendung denselben Dienst und dieselbe Datenbank verwenden, um Mandantendaten zu isolieren, eine BaseSaasEntity wird hier erstellt

Es gibt nur ein Feld „tenantId“ in
public abstract class BaseSaasEntity {
    @JsonIgnore
    @Column(nullable = false, updatable = false)
    protected Long tenantId;
    }

, das der Mieter-ID entspricht. Alle Mandanten-Geschäftseinheiten erben diese übergeordnete Klasse. Schließlich wird die „tenantId“ verwendet, um zu unterscheiden, zu welchem ​​Mandanten die Daten gehören.

SQL-Mandantendatenfilterung

Wie üblich sollte nach der Erstellung der Tabelle der CURD des entsprechenden Moduls befolgt werden. Die grundlegendste Anforderung für Saas-Anwendungen ist jedoch die Isolierung der Mandantendaten, d von: Fügen Sie wheremieter=? zu allen Mandantengeschäfts-SQL hinzu, um die Mandantendatenfilterung zu implementieren.

Hibernate-Filter

Wenn wir unserem Unternehmen Mandanten-SQL-Filtercode hinzufügen, ist nicht nur die Arbeitsbelastung enorm, sondern auch die Fehlerwahrscheinlichkeit hoch. Ideal ist es, das gefilterte SQL-Splicing zusammenzuarbeiten und die SQL-Filterung auf der Mandanten-Geschäftsschnittstelle zu aktivieren. Da JPA von Hibernate implementiert wird, können wir hier einige Funktionen von Hibernate nutzen. Hibernate-Filter sind global gültige, benannte Filter, die Parameter annehmen können. Sie können wählen, ob ein Filter für eine bestimmte Hibernate-Sitzung aktiviert (oder deaktiviert) werden soll.

Hier definieren wir eine SQL-Filterbedingung über @FilterDef und @Filter vor. Verwenden Sie dann eine @TenantFilter-Annotation, um zu identifizieren, dass die Schnittstelle eine Datenfilterung erfordert

@MappedSuperclass
@Data
@FilterDef(name = "tenantFilter", parameters = {@ParamDef(name = "tenantId", type = "long")})
@Filter(condition = "tenant_id=:tenantId", name = "tenantFilter")
public abstract class BaseSaasEntity {
    @JsonIgnore
    @Column(nullable = false, updatable = false)
    protected Long tenantId;


    @PrePersist
    public void onPrePersist() {
        if (getTenantId() != null) {
            return;
        }
        Long tenantId = TenantContext.getTenantId();
        Check.notNull(tenantId, "租户不存在");
        setTenantId(tenantId);
    }
}
Es ist ersichtlich, dass diese Schnittstelle auf der Methode platziert ist, die der Controller-Ebene entspricht. Die Bedeutung des Hinzufügens der Transaktionsanmerkung @Transactional besteht darin, dass eine Transaktion aktiviert sein muss, um den Ruhezustandsfilter zu aktivieren. Die Standardeinstellung ist hier eine schreibgeschützte Transaktion. Definieren Sie abschließend einen Aspekt, um den Filter zu aktivieren

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Transactional
public @interface TenantFilter {

    boolean readOnly() default true;

}

Das Objekt des Aspekts ist die gerade angepasste @TenantFilter-Anmerkung. Rufen Sie die aktuelle Mandanten-ID ab, bevor die Methode ausgeführt wird, und aktivieren Sie auf diese Weise die Mandantendatenisolierung Sie müssen nur die Geschäftsschnittstelle des Mandanten festlegen. Fügen Sie einfach die Annotation @TenantFilter hinzu, und die Entwicklung muss sich nur um den Geschäftscode kümmern. Der TenantContext im obigen Bild ist der aktuelle Thread-Mandantenkontext. Durch die Vereinbarung mit dem Front-End wird die Mandanten-ID zum Schnittstellenanforderungsheader hinzugefügt. Der Server verwendet den Interceptor, um die erhaltene Mandanten-ID in der ThreadLocal-Unterbibliothek zwischenzuspeichern

Mit zunehmender Anzahl von Mandanten werden die Daten in einer einzelnen MySQL-Datenbank und einer einzelnen Tabelle definitiv einen Engpass erreichen. Hier wird nur die Methode der Unterdatenbank verwendet. Nutzen Sie mehrere Datenquellen, um eine n:1-Zuordnung von Mandanten und Datenquellen durchzuführen.

@Aspect
@Slf4j
@RequiredArgsConstructor
public class TenantSQLAspect {
    private static final String FILTER_NAME = "tenantFilter";
    private final EntityManager entityManager;
    @SneakyThrows
    @Around("@annotation(com.lvjusoft.njcommon.annotation.TenantFilter)")
    public Object aspect(ProceedingJoinPoint joinPoint) {
        Session session = entityManager.unwrap(Session.class);
        try {
            Long tenantId = TenantContext.getTenantId();
            Check.notNull(tenantId, "租户不存在");
            session.enableFilter(FILTER_NAME).setParameter("tenantId", tenantId);
            return joinPoint.proceed();
        } finally {
            session.disableFilter(FILTER_NAME);
        }
    }
}

Deklarieren Sie eine dynamische Routing-Datenquelle, indem Sie AbstractRoutingDataSource implementieren. Bevor das Framework datesource verwendet, ruft Spring die Methode „determineCurrentLookupKey()“ auf, um zu bestimmen, welche Datenquelle verwendet werden soll. Der DataSourceContext ähnelt hier dem TenantContext oben. Nachdem Sie die TenantInfo im Interceptor erhalten haben, suchen Sie den Datenquellenschlüssel, der dem aktuellen Mandanten entspricht, und legen Sie ihn in ThreadLocal fest.

Das obige ist der detaillierte Inhalt vonSo erstellen Sie ein mandantenfähiges Springboot-SaaS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen