Skip to content

Commit 7cbc7c2

Browse files
authored
Merge pull request #572 from jeffgbutler/sharding-support
Add the ability to change a table name to AliasableSqlTable
2 parents 84df515 + 7253471 commit 7cbc7c2

File tree

14 files changed

+524
-109
lines changed

14 files changed

+524
-109
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ The pull request for this change is ([#550](https://github.com/mybatis/mybatis-d
4646
3. Refactored the common insert mapper support for MyBatis3 by adding a CommonGeneralInsertMapper that can be used
4747
without a class that matches the table row. It includes methods for general insert and insert select.
4848
([#570](https://github.com/mybatis/mybatis-dynamic-sql/pull/570))
49+
4. Added the ability to change a table name on AliasableSqlTable - this creates a new instance of the object with a new
50+
name. This is useful in sharded databases where the name of the table is calculated based on some sharding
51+
algorithm. Also deprecated the constructors on SqlTable that accept Suppliers for table name - this creates an
52+
effectively mutable object and goes against the principles of immutability that we strive for in the library.
53+
([#572](https://github.com/mybatis/mybatis-dynamic-sql/pull/572))
4954

5055

5156

src/main/java/org/mybatis/dynamic/sql/AliasableSqlTable.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,26 @@ protected AliasableSqlTable(String tableName, Supplier<T> constructor) {
2929
this.constructor = Objects.requireNonNull(constructor);
3030
}
3131

32-
protected AliasableSqlTable(Supplier<String> tableNameSupplier, Supplier<T> constructor) {
33-
super(tableNameSupplier);
34-
this.constructor = Objects.requireNonNull(constructor);
35-
}
36-
3732
public T withAlias(String alias) {
3833
T newTable = constructor.get();
3934
((AliasableSqlTable<T>) newTable).tableAlias = alias;
35+
newTable.nameSupplier = nameSupplier;
36+
return newTable;
37+
}
38+
39+
/**
40+
* Returns a new instance of this table with the specified name. All column instances are recreated.
41+
* This is useful for sharding where the table name may change at runtime based on some sharding algorithm,
42+
* but all other table attributes are the same.
43+
*
44+
* @param name new name for the table
45+
* @return a new AliasableSqlTable with the specified name, all other table attributes are copied
46+
*/
47+
public T withName(String name) {
48+
Objects.requireNonNull(name);
49+
T newTable = constructor.get();
50+
((AliasableSqlTable<T>) newTable).tableAlias = tableAlias;
51+
newTable.nameSupplier = () -> name;
4052
return newTable;
4153
}
4254

src/main/java/org/mybatis/dynamic/sql/SqlTable.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,48 @@
2424

2525
public class SqlTable implements TableExpression {
2626

27-
private final Supplier<String> nameSupplier;
27+
protected Supplier<String> nameSupplier;
2828

2929
protected SqlTable(String tableName) {
3030
Objects.requireNonNull(tableName);
3131

3232
this.nameSupplier = () -> tableName;
3333
}
3434

35+
/**
36+
* Creates an SqlTable whose name can be changed at runtime.
37+
*
38+
* @param tableNameSupplier table name supplier
39+
* @deprecated please use {@link AliasableSqlTable} if you need to change the table name at runtime
40+
*/
41+
@Deprecated
3542
protected SqlTable(Supplier<String> tableNameSupplier) {
3643
Objects.requireNonNull(tableNameSupplier);
3744

3845
this.nameSupplier = tableNameSupplier;
3946
}
4047

48+
/**
49+
* Creates an SqlTable whose name can be changed at runtime.
50+
*
51+
* @param schemaSupplier schema supplier
52+
* @param tableName table name
53+
* @deprecated please use {@link AliasableSqlTable} if you need to change the table name at runtime
54+
*/
55+
@Deprecated
4156
protected SqlTable(Supplier<Optional<String>> schemaSupplier, String tableName) {
4257
this(Optional::empty, schemaSupplier, tableName);
4358
}
4459

60+
/**
61+
* Creates an SqlTable whose name can be changed at runtime.
62+
*
63+
* @param catalogSupplier catalog supplier
64+
* @param schemaSupplier schema supplier
65+
* @param tableName table name
66+
* @deprecated please use {@link AliasableSqlTable} if you need to change the table name at runtime
67+
*/
68+
@Deprecated
4569
protected SqlTable(Supplier<Optional<String>> catalogSupplier, Supplier<Optional<String>> schemaSupplier,
4670
String tableName) {
4771
Objects.requireNonNull(catalogSupplier);

src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion, AndOrC
561561
return having(initialCriterion, Arrays.asList(subCriteria));
562562
}
563563

564-
public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
564+
public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion,
565+
List<AndOrCriteriaGroup> subCriteria) {
565566
return QueryExpressionDSL.this.having(initialCriterion, subCriteria);
566567
}
567568
}
@@ -599,7 +600,8 @@ public FromGatherer<R> selectDistinct(List<BasicColumn> selectList) {
599600
}
600601
}
601602

602-
public class QueryExpressionHavingBuilder extends AbstractBooleanExpressionDSL<QueryExpressionHavingBuilder> implements Buildable<R> {
603+
public class QueryExpressionHavingBuilder extends AbstractBooleanExpressionDSL<QueryExpressionHavingBuilder>
604+
implements Buildable<R> {
603605

604606
public QueryExpressionHavingBuilder(SqlCriterion initialCriterion) {
605607
setInitialCriterion(initialCriterion);

src/site/markdown/docs/databaseObjects.md

Lines changed: 23 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ MyBatis Dynamic SQL works with Java objects that represent relational tables or
44
## Table or View Representation
55

66
The class `org.mybatis.dynamic.sql.SqlTable` is used to represent a table or view in a database. An `SqlTable` holds a
7-
name, and a collection of `SqlColumn` objects that represent the columns in a table or view.
7+
name, and a collection of `SqlColumn` objects that represent the columns in a table or view. A subclass of `SqlTable` -
8+
`AliasableSqlTable` should be used in cases where you want to specify a table alias that should be used in all cases,
9+
or if you need to change the table name at runtime.
810

911
A table or view name in SQL has three parts:
1012

1113
1. The catalog - which is optional and is rarely used outside of Microsoft SQL Server. If unspecified the default
1214
catalog will be used - and many databases only have one catalog
13-
1. The schema - which is optional but is very often specified. If unspecified, the default schema will be used
14-
1. The table name - which is required
15+
2. The schema - which is optional but is very often specified. If unspecified, the default schema will be used
16+
3. The table name - which is required
1517

1618
Typical examples of names are as follows:
1719

@@ -21,126 +23,48 @@ Typical examples of names are as follows:
2123
- `"bar"` - a name with just a table name (bar). This will access a table or view in the default catalog and schema for
2224
a connection
2325

24-
In MyBatis Dynamic SQL, the table or view name can be specified in different ways:
26+
In MyBatis Dynamic SQL, the full name of the table should be supplied on the constructor of the table object.
27+
If a table name needs to change at runtime (say for sharding support), then use the `withName` method on
28+
`AliasableSqlTable` to create an instance with the new name.
2529

26-
1. The name can be a constant String
27-
1. The name can be calculated at runtime based on a catalog and/or schema supplier functions and a constant table name
28-
1. The name can be calculated at runtime with a name supplier function
30+
We recommend using the base class `AliasableSqlTable` in all cases as it provides the most flexibility. The
31+
`SqlTable` class remains in the library for compatibility with older code only.
2932

30-
### Constant Names
31-
32-
Constant names are used when you use the `SqlTable` constructor with a single String argument. For example:
33+
For example:
3334

3435
```java
35-
public class MyTable extends SqlTable {
36+
import org.mybatis.dynamic.sql.AliasableSqlTable;
37+
38+
public class MyTable extends AliasableSqlTable<MyTable> {
3639
public MyTable() {
37-
super("MyTable");
40+
super("MyTable", MyTable::new);
3841
}
3942
}
4043
```
4144

4245
Or
4346

4447
```java
45-
public class MyTable extends SqlTable {
48+
public class MyTable extends AliasableSqlTable<MyTable> {
4649
public MyTable() {
47-
super("MySchema.MyTable");
48-
}
49-
}
50-
```
51-
52-
### Dynamic Catalog and/or Schema Names
53-
MyBatis Dynamic SQL allows you to dynamically specify a catalog and/or schema. This is useful for applications where
54-
the schema may change for different users or environments, or if you are using different schemas to shard the database.
55-
Dynamic names are used when you use a `SqlTable` constructor that accepts one or more `java.util.function.Supplier`
56-
arguments.
57-
58-
For example, suppose you wanted to change the schema based on the value of a system property. You could write a class
59-
like this:
60-
61-
```java
62-
public class SchemaSupplier {
63-
public static final String schema_property = "schemaToUse";
64-
65-
public static Optional<String> schemaPropertyReader() {
66-
return Optional.ofNullable(System.getProperty(schema_property));
50+
super("MySchema.MyTable", MyTable::new);
6751
}
6852
}
6953
```
7054

71-
This class has a static method `schemaPropertyReader` that will return an `Optional<String>` containing the value of a
72-
system property. You could then reference this method in the constructor of the `SqlTable` like this:
73-
74-
```java
75-
public static final class User extends SqlTable {
76-
public User() {
77-
super(SchemaSupplier::schemaPropertyReader, "User");
78-
}
79-
}
80-
```
81-
82-
Whenever the table is referenced for rendering SQL, the name will be calculated based on the current value of the
83-
system property.
84-
85-
There are two constructors that can be used for dynamic names:
86-
87-
1. A constructor that accepts `Supplier<Optional<String>>` for the schema, and `String` for the name. This constructor
88-
assumes that the catalog is always empty or not used
89-
1. A constructor that accepts `Supplier<Optional<String>>` for the catalog, `Supplier<Optional<String>>` for the schema,
90-
and `String` for the name
91-
92-
If you are using Microsoft SQL Server and want to use a dynamic catalog name and ignore the schema, then you should use
93-
the second constructor like this:
55+
You can change a table name:
9456

9557
```java
96-
public static final class User extends SqlTable {
97-
public User() {
98-
super(CatalogSupplier::catalogPropertyReader, Optional::empty, "User");
99-
}
100-
}
101-
```
102-
103-
The following table shows how the name is calculated in all combinations of suppliers:
104-
105-
Catalog Supplier Value | Schema Supplier Value | Name | Fully Qualified Name
106-
---|---|---|---
107-
"MyCatalog" | "MySchema" | "MyTable" | "MyCatalog.MySchema.MyTable"
108-
&lt;empty&gt; | "MySchema" | "MyTable" | "MySchema.MyTable"
109-
"MyCatalog" | &lt;empty&gt; | "MyTable" | "MyCatalog..MyTable"
110-
&lt;empty&gt; | &lt;empty&gt; | "MyTable" | "MyTable"
111-
112-
113-
### Fully Dynamic Names
114-
MyBatis Dynamic SQL allows you to dynamically specify a full table name. This is useful for applications where the
115-
database is sharded with different tables representing different shards of the whole. Dynamic names are used when you
116-
use a `SqlTable` constructor that accepts a single `java.util.function.Supplier` argument.
117-
118-
For example, suppose you wanted to change the name based on the value of a system property. You could write a class
119-
like this:
120-
121-
```java
122-
public class NameSupplier {
123-
public static final String name_property = "nameToUse";
124-
125-
public static String namePropertyReader() {
126-
return System.getProperty(name_property);
58+
public class MyTable extends AliasableSqlTable<MyTable> {
59+
public MyTable() {
60+
super("Schema1.MyTable", MyTable::new);
12761
}
12862
}
129-
```
130-
131-
This class has a static method `namePropertyReader` that will return an `String` containing the value of a system
132-
property. You could then reference this method in the constructor of the `SqlTable` like this:
13363

134-
```java
135-
public static final class User extends SqlTable {
136-
public User() {
137-
super(NameSupplier::namePropertyReader);
138-
}
139-
}
64+
MyTable schema1Table = new MyTable();
65+
MyTable schema2Table = schema1Table.withName("Schema2.MyTable");
14066
```
14167

142-
Whenever the table is referenced for rendering SQL, the name will be calculated based on the current value of the system property.
143-
14468
## Aliased Tables
14569

14670
In join queries, it is usually a good practice to specify table aliases. The `select` statement includes

src/test/java/examples/joins/UserDynamicSQLSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static final class User extends AliasableSqlTable<User> {
3232
public final SqlColumn<Integer> parentId = column("parent_id", JDBCType.INTEGER);
3333

3434
public User() {
35-
super(() -> "User", User::new);
35+
super("User", User::new);
3636
}
3737
}
3838
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2016-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package examples.sharding;
17+
18+
import org.mybatis.dynamic.sql.util.mybatis3.CommonCountMapper;
19+
import org.mybatis.dynamic.sql.util.mybatis3.CommonGeneralInsertMapper;
20+
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
21+
22+
public interface ShardedMapper extends CommonCountMapper, CommonGeneralInsertMapper, CommonSelectMapper {
23+
}

0 commit comments

Comments
 (0)