Skip to content

Commit a22604a

Browse files
authored
Merge pull request #48452 from yrodiere/mariadb-schema-multitenancy
Document and test schema-based multitenancy on MariaDB and MySQL
2 parents 9d60764 + 53f58c0 commit a22604a

File tree

17 files changed

+988
-3
lines changed

17 files changed

+988
-3
lines changed

.github/native-tests.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
},
3333
{
3434
"category": "Data5",
35-
"timeout": 70,
36-
"test-modules": "jpa-postgresql, jpa-postgresql-withxml, narayana-stm, narayana-jta, reactive-pg-client, hibernate-reactive-postgresql, hibernate-orm-tenancy/schema",
35+
"timeout": 75,
36+
"test-modules": "jpa-postgresql, jpa-postgresql-withxml, narayana-stm, narayana-jta, reactive-pg-client, hibernate-reactive-postgresql, hibernate-orm-tenancy/schema, hibernate-orm-tenancy/schema-mariadb",
3737
"os-name": "ubuntu-latest"
3838
},
3939
{

docs/src/main/asciidoc/hibernate-orm.adoc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,17 @@ The following setup will use the xref:flyway.adoc[Flyway] extension to achieve t
12351235

12361236
The same data source will be used for all tenants and a schema has to be created for every tenant inside that data source.
12371237

1238-
CAUTION: Some databases like MariaDB/MySQL do not support database schemas. In these cases you have to use the <<database-approach,database approach>>.
1238+
CAUTION: Some databases like MariaDB/MySQL do not support database schemas by default. In these cases you can either:
1239+
1. Configure the JDBC driver to support schemas.
1240+
Use `quarkus.datasource.jdbc.additional-jdbc-properties."databaseTerm"=SCHEMA`
1241+
or `quarkus.datasource."datasource-name".jdbc.additional-jdbc-properties."databaseTerm"=SCHEMA`
1242+
for https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-connection.html#cj-conn-prop_databaseTerm[MySQL Connector/J].
1243+
Use `quarkus.datasource.jdbc.additional-jdbc-properties."useCatalogTerm"=SCHEMA`
1244+
or `quarkus.datasource."datasource-name".jdbc.additional-jdbc-properties."useCatalogTerm"=SCHEMA`
1245+
for https://mariadb.com/docs/connectors/mariadb-connector-j/about-mariadb-connector-j[MariaDB Connector/J].
1246+
1247+
and
1248+
2. Fall back to the <<database-approach,database approach>>.
12391249

12401250
[source,properties]
12411251
----

integration-tests/hibernate-orm-tenancy/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<module>datasource</module>
1818
<module>connection-resolver</module>
1919
<module>schema</module>
20+
<module>schema-mariadb</module>
2021
<module>discriminator</module>
2122
<module>connection-resolver-legacy-qualifiers</module>
2223
</modules>
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
6+
<parent>
7+
<artifactId>quarkus-integration-test-hibernate-orm-tenancy</artifactId>
8+
<groupId>io.quarkus</groupId>
9+
<version>999-SNAPSHOT</version>
10+
</parent>
11+
<modelVersion>4.0.0</modelVersion>
12+
13+
<artifactId>quarkus-integration-test-hibernate-orm-tenancy-schema-mariadb</artifactId>
14+
<name>Quarkus - Integration Tests - Hibernate - Tenancy - Schema - MariaDB</name>
15+
<description>Tests for Hibernate ORM Multitenancy using one schema per tenant, running with the MariaDB database</description>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>io.quarkus</groupId>
20+
<artifactId>quarkus-undertow</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>io.quarkus</groupId>
24+
<artifactId>quarkus-hibernate-orm</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>io.quarkus</groupId>
28+
<artifactId>quarkus-jdbc-mariadb</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>io.quarkus</groupId>
32+
<artifactId>quarkus-flyway</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.flywaydb</groupId>
36+
<artifactId>flyway-mysql</artifactId>
37+
</dependency>
38+
<dependency>
39+
<groupId>io.quarkus</groupId>
40+
<artifactId>quarkus-resteasy</artifactId>
41+
</dependency>
42+
<dependency>
43+
<groupId>io.quarkus</groupId>
44+
<artifactId>quarkus-resteasy-jackson</artifactId>
45+
</dependency>
46+
47+
<!-- test dependencies -->
48+
<dependency>
49+
<groupId>io.quarkus</groupId>
50+
<artifactId>quarkus-junit5</artifactId>
51+
<scope>test</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>io.rest-assured</groupId>
55+
<artifactId>rest-assured</artifactId>
56+
<scope>test</scope>
57+
</dependency>
58+
59+
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
60+
<dependency>
61+
<groupId>io.quarkus</groupId>
62+
<artifactId>quarkus-flyway-deployment</artifactId>
63+
<version>${project.version}</version>
64+
<type>pom</type>
65+
<scope>test</scope>
66+
<exclusions>
67+
<exclusion>
68+
<groupId>*</groupId>
69+
<artifactId>*</artifactId>
70+
</exclusion>
71+
</exclusions>
72+
</dependency>
73+
<dependency>
74+
<groupId>io.quarkus</groupId>
75+
<artifactId>quarkus-hibernate-orm-deployment</artifactId>
76+
<version>${project.version}</version>
77+
<type>pom</type>
78+
<scope>test</scope>
79+
<exclusions>
80+
<exclusion>
81+
<groupId>*</groupId>
82+
<artifactId>*</artifactId>
83+
</exclusion>
84+
</exclusions>
85+
</dependency>
86+
<dependency>
87+
<groupId>io.quarkus</groupId>
88+
<artifactId>quarkus-jdbc-mariadb-deployment</artifactId>
89+
<version>${project.version}</version>
90+
<type>pom</type>
91+
<scope>test</scope>
92+
<exclusions>
93+
<exclusion>
94+
<groupId>*</groupId>
95+
<artifactId>*</artifactId>
96+
</exclusion>
97+
</exclusions>
98+
</dependency>
99+
<dependency>
100+
<groupId>io.quarkus</groupId>
101+
<artifactId>quarkus-resteasy-deployment</artifactId>
102+
<version>${project.version}</version>
103+
<type>pom</type>
104+
<scope>test</scope>
105+
<exclusions>
106+
<exclusion>
107+
<groupId>*</groupId>
108+
<artifactId>*</artifactId>
109+
</exclusion>
110+
</exclusions>
111+
</dependency>
112+
<dependency>
113+
<groupId>io.quarkus</groupId>
114+
<artifactId>quarkus-resteasy-jackson-deployment</artifactId>
115+
<version>${project.version}</version>
116+
<type>pom</type>
117+
<scope>test</scope>
118+
<exclusions>
119+
<exclusion>
120+
<groupId>*</groupId>
121+
<artifactId>*</artifactId>
122+
</exclusion>
123+
</exclusions>
124+
</dependency>
125+
<dependency>
126+
<groupId>io.quarkus</groupId>
127+
<artifactId>quarkus-undertow-deployment</artifactId>
128+
<version>${project.version}</version>
129+
<type>pom</type>
130+
<scope>test</scope>
131+
<exclusions>
132+
<exclusion>
133+
<groupId>*</groupId>
134+
<artifactId>*</artifactId>
135+
</exclusion>
136+
</exclusions>
137+
</dependency>
138+
</dependencies>
139+
140+
<build>
141+
<resources>
142+
<resource>
143+
<directory>src/main/resources</directory>
144+
<filtering>true</filtering>
145+
</resource>
146+
</resources>
147+
<plugins>
148+
<plugin>
149+
<artifactId>maven-surefire-plugin</artifactId>
150+
<configuration>
151+
<skip>true</skip>
152+
</configuration>
153+
</plugin>
154+
<plugin>
155+
<artifactId>maven-failsafe-plugin</artifactId>
156+
<configuration>
157+
<skip>true</skip>
158+
</configuration>
159+
</plugin>
160+
<plugin>
161+
<groupId>io.quarkus</groupId>
162+
<artifactId>quarkus-maven-plugin</artifactId>
163+
<executions>
164+
<execution>
165+
<goals>
166+
<goal>build</goal>
167+
</goals>
168+
</execution>
169+
</executions>
170+
</plugin>
171+
</plugins>
172+
</build>
173+
174+
<profiles>
175+
<profile>
176+
<id>test-mariadb</id>
177+
<activation>
178+
<property>
179+
<name>test-containers</name>
180+
</property>
181+
</activation>
182+
<build>
183+
<plugins>
184+
<plugin>
185+
<artifactId>maven-surefire-plugin</artifactId>
186+
<configuration>
187+
<skip>false</skip>
188+
</configuration>
189+
</plugin>
190+
<plugin>
191+
<artifactId>maven-failsafe-plugin</artifactId>
192+
<configuration>
193+
<skip>false</skip>
194+
</configuration>
195+
</plugin>
196+
</plugins>
197+
</build>
198+
</profile>
199+
</profiles>
200+
201+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.quarkus.it.hibernate.multitenancy.fruit;
2+
3+
import jakarta.enterprise.context.RequestScoped;
4+
import jakarta.inject.Inject;
5+
6+
import org.jboss.logging.Logger;
7+
8+
import io.quarkus.hibernate.orm.PersistenceUnitExtension;
9+
import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
10+
import io.vertx.ext.web.RoutingContext;
11+
12+
@PersistenceUnitExtension
13+
@RequestScoped
14+
public class CustomTenantResolver implements TenantResolver {
15+
16+
private static final Logger LOG = Logger.getLogger(CustomTenantResolver.class);
17+
18+
@Inject
19+
RoutingContext context;
20+
21+
@Override
22+
public String getDefaultTenantId() {
23+
return "base";
24+
}
25+
26+
@Override
27+
public String resolveTenantId() {
28+
String path = context.request().path();
29+
final String tenantId;
30+
if (path.startsWith("/mycompany")) {
31+
tenantId = "mycompany";
32+
} else {
33+
tenantId = getDefaultTenantId();
34+
}
35+
LOG.debugv("TenantId = {0}", tenantId);
36+
return tenantId;
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package io.quarkus.it.hibernate.multitenancy.fruit;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.GeneratedValue;
6+
import jakarta.persistence.GenerationType;
7+
import jakarta.persistence.Id;
8+
import jakarta.persistence.NamedQuery;
9+
import jakarta.persistence.SequenceGenerator;
10+
import jakarta.persistence.Table;
11+
import jakarta.xml.bind.annotation.XmlRootElement;
12+
13+
@Entity
14+
@Table(name = "known_fruits")
15+
@NamedQuery(name = "Fruits.findAll", query = "SELECT f FROM Fruit f ORDER BY f.name")
16+
@NamedQuery(name = "Fruits.findByName", query = "SELECT f FROM Fruit f WHERE f.name=:name")
17+
@XmlRootElement(name = "fruit")
18+
public class Fruit {
19+
20+
@Id
21+
@SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
22+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "fruitsSequence")
23+
private Integer id;
24+
25+
@Column(length = 40, unique = true)
26+
private String name;
27+
28+
public Fruit() {
29+
}
30+
31+
public Fruit(String name) {
32+
this.name = name;
33+
}
34+
35+
public Fruit(Integer id, String name) {
36+
this.id = id;
37+
this.name = name;
38+
}
39+
40+
public Integer getId() {
41+
return id;
42+
}
43+
44+
public void setId(Integer id) {
45+
this.id = id;
46+
}
47+
48+
public String getName() {
49+
return name;
50+
}
51+
52+
public void setName(String name) {
53+
this.name = name;
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
final int prime = 31;
59+
int result = 1;
60+
result = prime * result + ((id == null) ? 0 : id.hashCode());
61+
result = prime * result + ((name == null) ? 0 : name.hashCode());
62+
return result;
63+
}
64+
65+
@Override
66+
public boolean equals(Object obj) {
67+
if (this == obj)
68+
return true;
69+
if (obj == null)
70+
return false;
71+
if (getClass() != obj.getClass())
72+
return false;
73+
Fruit other = (Fruit) obj;
74+
if (id == null) {
75+
if (other.id != null)
76+
return false;
77+
} else if (!id.equals(other.id))
78+
return false;
79+
if (name == null) {
80+
if (other.name != null)
81+
return false;
82+
} else if (!name.equals(other.name))
83+
return false;
84+
return true;
85+
}
86+
87+
@Override
88+
public String toString() {
89+
return "Fruit [id=" + id + ", name=" + name + "]";
90+
}
91+
92+
}

0 commit comments

Comments
 (0)