Ich hab eine Multitenancy-Funktion aus GitHub in mein Programm eingebaut. Aus irgendeinem Grund kann ich aber über save nichts mehr speichern. Die repository.save(myObj) wird einfach beim Debuggen ausgeführt, aber in der Datenbank kommt nichts an. Ich vermute diese Erweiterung aktiviert irgendwas, sodass ich mit Transactions arbeiten muss. Aber wie ?! Hat jemand ne Idee was solch ein Verhalten auslösen kann?
Hier eine (wichtige) Klasse, die die Datenbankverbindungen zu den Mandanten aufbaut. Mein Repository ist ein ganz normales und dieses wird @Autowired in den Controller geladen.
@GetMapping("/getBrand/{brandName}")
public Brand getBrand(@RequestHeader("tenant") String tenant, @PathVariable String brandName) {
TenantContextHolder.setTenantId(tenant);
Brand b = brandRepository.findByBrandName(brandName); << null, wenn Brand noch nicht existiert
if (b == null) {
Brand brand = new Brand();
brand.setbrandName(brandName);
Brand savedBrand = brandRepository.save(brand); <<<wird nicht in DB gespeichert, kein Fehler
return savedBrand;
}
return b;
}
Da der Controller bereits instantiiert ist und somit auch die DB Verbindung, wird sicherlich etwas in einer DB geschrieben, aber nicht in der du gerade den Eintrag erwartest.
Grund hierür ist, dass das auslesen der Header Tenant Variable vor dem Aufruf des Controllers passieren muss. Hier ist es vermutlich zu spät und sehr „anstrengend“, da jeder Controller diese Logik implementieren muss.
In dem anderen Beispiel dürfte ein Filter vor dem Controller den Tenant für den Thread zur Ausführung setzen.
geschrieben wird nix, die Datenbanken bleiben leer. Was jedoch passiert; hibernate_sequence zählt die id hoch. Wenn ich mit Postman den BrandController Anfrage wird mir die Brand zurück gegeben, obwohl sie nicht geschrieben wurde. Hibernate vergibt aber eine ID. Rufe ich die selbe Brand wieder ab, erhöht sich die ID, weil die Brand nicht gefunden werden kann und nochmal „gespeichert“ wird (aber nicht ankommt)
Wenn ich manuell werte in de DB speichere und Abrufe, kommen die richtigen Werte aus der entsprechenden DB. Mit dem Filter Versuch ich mal beim debuggen hat es jedoch die korrekte tenantID
Hi, ja das weiß ich, dass keine neuen Erkenntnisse drin waren:)
Da du so ziemlich sicher schriebst, dass es am fehlenden Filter liegt, wollte ich das erstmal testen. Der restliche Code ist hier zu finden, das ist Der Code den ich eingebunden hab https://github.com/sunitk/multitenancy-dynamic-tenant
Lass mal den ganzen Tentant-Kram weg, sodass er nur auf einer einzelnen Datenbank arbeitet - speichert er dann korrekt?
Laut dir schreibt er ja in die passende DB (zumindest hibernate_sequence) und aus der passenden wird gelesen, wenn du selbst was reinschreibst.
Lässt du SQL loggen? Zumindest irgendwas zu hibernate_sequence sollte dort zu sehen sein - und wenn ja: nur das?
Gibt es irgendwelche Exceptions?
Und ist dein gesamter Code irgendwo verfügbar?
Mit Teilstücken kann man da schlecht den Fehler finden, der kann überall versteckt sein…
Als ich diesen teanten Kram noch nicht drin hatte, schrieb es. Habe nun mal meine Entity auf IDENTITY gesetzt, sodass die DB nun mittels auto_increment die ID setzt, richtig?
Wenn ich manuell eine Brand eintrage und die abrufe, bekomme ich die korrekten Werte, samt meiner vergebenen ID. Meinen Code gibts leider nirgendwo. Könnte benötigtes aber posten, wenn ich weiß was genau
Dann wird nur folgendes Hibernatestatement ausgegeben (aber warum 2 mal?)
select
brand0_.id as id1_0_,
brand0_.brand_name as brand_na2_0_
from
brand brand0_
where
brand0_.brand_name=?
Hibernate:
select
brand0_.id as id1_0_,
brand0_.brand_name as brand_na2_0_
from
brand brand0_
where
brand0_.brand_name=?
In der oben verlinkten Klasse, wird eine Bean erstellt: @Bean(name = “tenantEntityManagerFactory”) @ConditionalOnBean(name = “datasourceBasedMultitenantConnectionProvider”)
public LocalContainerEntityManagerFactoryBean entityManagerFactory( @Qualifier(“datasourceBasedMultitenantConnectionProvider”) MultiTenantConnectionProvider connectionProvider, @Qualifier(“currentTenantIdentifierResolver”) CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
// All tenant related entities, repositories and service classes must be scanned
emfBean.setPackagesToScan(new String[] { "com.domain.model.tenancy", "com.domain.model" });
emfBean.setJpaVendorAdapter(jpaVendorAdapter());
emfBean.setPersistenceUnitName("tenantdb-persistence-unit");
Map<String, Object> properties = new HashMap<>();
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
// ImprovedNamingStrategy is deprecated and unsupported in Hibernate 5
// properties.put("hibernate.ejb.naming_strategy",
// "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put(org.hibernate.cfg.Environment.DIALECT, "org.hibernate.dialect.MySQL5Dialect");
properties.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
properties.put(org.hibernate.cfg.Environment.FORMAT_SQL, true);
properties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "update");
emfBean.setJpaPropertyMap(properties);
LOG.info("tenantEntityManagerFactory set up successfully!");
return emfBean;
}
Muss man diese irgendwie angeben mit @Transactional? in der Klasse, wo .save aufgerufen wird?
beim „nicht“-speichern wird das selbe ausgegeben, da ja erst geprüft wird ob schon vorhanden. das .save() an sich gibts nichts aus Einfach nichts…kein INFO, DEBUG einfach nichts Ich werd mal schauen, dass ich den wichtigsten Code hochlade irgenwie.
22:45:46 DEBUG [http-nio-8182-exec-2] -
select
brand0_.id as id1_0_,
brand0_.brand_name as brand_na2_0_
from
brand brand0_
where
brand0_.brand_name=?
Hibernate:
select
brand0_.id as id1_0_,
brand0_.brand_name as brand_na2_0_
from
brand brand0_
where
brand0_.brand_name=?
22:45:46 DEBUG [http-nio-8182-exec-2] - committing
22:45:46 DEBUG [http-nio-8182-exec-2] - begin
22:45:46 DEBUG [http-nio-8182-exec-2] - committing
In der folgenden Methode musste ein Qualifier angegeben werden, nun klappt es. Was auch immer das zu bedeuten hat, muss ich erstmal lernen. Ich danke euch allen wieder für eure tatkräftige Unterstützung! Eine Spendenfunktion gibt es in diesem Forum nicht oder?
@Bean(name = "tenantTransactionManager")
public JpaTransactionManager transactionManager(@Qualifier("tenantEntityManagerFactory") EntityManagerFactory tenantEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(tenantEntityManager);
return transactionManager;
}
Edit: noch eine unabhängige Frage von dem Problem. @GeneratedValue(strategy = GenerationType.IDENTITY)
oder AUTO? Identity empfinde ich als schneller, der hat AUTO irgendwelche Vorteile die ich noch nicht kenne?
Transaktionen sind per DB Connection, wenn da mehrere offen sind, und das ist immer der Fall, ohne tenants wegen pooling, mit Tenants dann pooling und per tenant (Tenants haben ja ihre eigene DB), wenn die Transaktionen nicht committed wird, wird nix gespeichert, entweder es kommt nix in der DB an (performance Optimierung), oder the DB bekommt alle SQL statements aber eben kein commit.
Wenn man mit JDBC und RDBMS nicht vertraut ist, sind ORMs nur verwirrend.
Zu „empfinden“ ist immer wichtig, ausser wenn es um performance geht, dann zaehlt nur messen, wenn du nicht weisst was der Unterschied zwischen AUTO und IDENTITY ist, konzentriere dich erstmal darauf