Heim >Java >javaLernprogramm >So erstellen Sie ein mandantenfähiges Springboot-SaaS
Springboot-Version ist 2.3.4.RELEASE
Die Persistenzschicht übernimmt JPA
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“ inpublic 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.
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.
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.
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 aktivierenHier 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); } }
@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!