Skip to content

Commit 32de52f

Browse files
committed
Merge branch 'schema-provider'
2 parents 261866a + 0b9b087 commit 32de52f

File tree

22 files changed

+679
-116
lines changed

22 files changed

+679
-116
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=miles
99
### Added
1010

1111
- Changed the public SQLBuilder API to accept Collection instead of List for in conditions and batch record inserts. This should have no impact on existing code, but allow for some future flexibility
12+
- Added the ability have have table catalog and/or schema calculated at query runtime. This is useful for situations where there are different database schemas for different environments, or in some sharding situations
1213

1314

1415
## Release 1.1.1 - April 7, 2019

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

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2018 the original author or authors.
2+
* Copyright 2016-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,17 +17,60 @@
1717

1818
import java.sql.JDBCType;
1919
import java.util.Objects;
20+
import java.util.Optional;
21+
import java.util.function.Supplier;
2022

2123
public class SqlTable {
24+
25+
private Supplier<String> nameSupplier;
26+
27+
protected SqlTable(String fullyQualifiedTableName) {
28+
Objects.requireNonNull(fullyQualifiedTableName);
29+
30+
this.nameSupplier = () -> fullyQualifiedTableName;
31+
}
32+
33+
protected SqlTable(Supplier<Optional<String>> schemaSupplier, String tableName) {
34+
this(Optional::empty, schemaSupplier, tableName);
35+
}
36+
37+
protected SqlTable(Supplier<Optional<String>> catalogSupplier, Supplier<Optional<String>> schemaSupplier, String tableName) {
38+
Objects.requireNonNull(catalogSupplier);
39+
Objects.requireNonNull(schemaSupplier);
40+
Objects.requireNonNull(tableName);
41+
42+
this.nameSupplier = () -> compose(catalogSupplier, schemaSupplier, tableName);
43+
}
44+
45+
private String compose(Supplier<Optional<String>> catalogSupplier, Supplier<Optional<String>> schemaSupplier, String tableName) {
46+
return catalogSupplier.get().map(c -> compose(c, schemaSupplier, tableName))
47+
.orElse(compose(schemaSupplier, tableName));
48+
}
49+
50+
private String compose(String catalog, Supplier<Optional<String>> schemaSupplier, String tableName) {
51+
return schemaSupplier.get().map(s -> composeCatalogSchemaAndAndTable(catalog, s, tableName))
52+
.orElse(composeCatalogAndTable(catalog, tableName));
53+
}
2254

23-
private String name;
55+
private String compose(Supplier<Optional<String>> schemaSupplier, String tableName) {
56+
return schemaSupplier.get().map(s -> composeSchemaAndTable(s, tableName))
57+
.orElse(tableName);
58+
}
59+
60+
private String composeCatalogAndTable(String catalog, String tableName) {
61+
return catalog + ".." + tableName; //$NON-NLS-1$
62+
}
2463

25-
protected SqlTable(String name) {
26-
this.name = Objects.requireNonNull(name);
64+
private String composeSchemaAndTable(String schema, String tableName) {
65+
return schema + "." + tableName; //$NON-NLS-1$
2766
}
2867

29-
public String name() {
30-
return name;
68+
private String composeCatalogSchemaAndAndTable(String catalog, String schema, String tableName) {
69+
return catalog + "." + schema + "." + tableName; //$NON-NLS-1$ //$NON-NLS-2$
70+
}
71+
72+
public String fullyQualifiedTableName() {
73+
return nameSupplier.get();
3174
}
3275

3376
public <T> SqlColumn<T> allColumns() {

src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ private Optional<WhereClauseProvider> renderWhereClause(WhereModel whereModel) {
5858

5959
private String calculateDeleteStatement(Optional<WhereClauseProvider> whereClause) {
6060
return "delete from" //$NON-NLS-1$
61-
+ spaceBefore(deleteModel.table().name())
61+
+ spaceBefore(deleteModel.table().fullyQualifiedTableName())
6262
+ spaceBefore(whereClause.map(WhereClauseProvider::getWhereClause));
6363
}
6464

src/main/java/org/mybatis/dynamic/sql/insert/render/BatchInsertRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, InsertMapping
5454

5555
private String calculateInsertStatement(FieldAndValueCollector<T> collector) {
5656
return "insert into" //$NON-NLS-1$
57-
+ spaceBefore(model.table().name())
57+
+ spaceBefore(model.table().fullyQualifiedTableName())
5858
+ spaceBefore(collector.columnsPhrase())
5959
+ spaceBefore(collector.valuesPhrase());
6060
}

src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public InsertStatementProvider<T> render() {
4646

4747
private String calculateInsertStatement(FieldAndValueCollector<T> collector) {
4848
return "insert into" //$NON-NLS-1$
49-
+ spaceBefore(model.table().name())
49+
+ spaceBefore(model.table().fullyQualifiedTableName())
5050
+ spaceBefore(collector.columnsPhrase())
5151
+ spaceBefore(collector.valuesPhrase());
5252
}

src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public InsertSelectStatementProvider render() {
4747

4848
private String calculateInsertStatement(SelectStatementProvider selectStatement) {
4949
return "insert into" //$NON-NLS-1$
50-
+ spaceBefore(model.table().name())
50+
+ spaceBefore(model.table().fullyQualifiedTableName())
5151
+ spaceBefore(calculateColumnsPhrase())
5252
+ spaceBefore(selectStatement.getSelectStatement());
5353
}

src/main/java/org/mybatis/dynamic/sql/render/GuaranteedTableAliasCalculator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2017 the original author or authors.
2+
* Copyright 2016-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ private GuaranteedTableAliasCalculator(Map<SqlTable, String> aliases) {
3737
public Optional<String> aliasForColumn(SqlTable table) {
3838
return super.aliasForColumn(table)
3939
.map(Optional::of)
40-
.orElse(Optional.of(table.name()));
40+
.orElse(Optional.of(table.fullyQualifiedTableName()));
4141
}
4242

4343
public static TableAliasCalculator of(Map<SqlTable, String> aliases) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2018 the original author or authors.
2+
* Copyright 2016-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -88,7 +88,7 @@ public Optional<GroupByModel> groupByModel() {
8888
}
8989

9090
public String calculateTableNameIncludingAlias(SqlTable table) {
91-
return table.name()
91+
return table.fullyQualifiedTableName()
9292
+ spaceBefore(tableAliasCalculator.aliasForTable(table));
9393
}
9494

src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private FragmentCollector calculateColumnMappings() {
6565

6666
private String calculateUpdateStatement(FragmentCollector fc, Optional<WhereClauseProvider> whereClause) {
6767
return "update" //$NON-NLS-1$
68-
+ spaceBefore(updateModel.table().name())
68+
+ spaceBefore(updateModel.table().fullyQualifiedTableName())
6969
+ spaceBefore(calculateSetPhrase(fc))
7070
+ spaceBefore(whereClause.map(WhereClauseProvider::getWhereClause));
7171
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Database Object Representation
2+
MyBatis Dynamic SQL works with Java objects that represent relational tables or views.
3+
4+
## Table or View Representation
5+
6+
The class `SqlTable` is used to represent a table or view in a database. An `SqlTable` holds a name, and a collection of `SqlColumn` objects that represent the columns in a table or view.
7+
8+
A fully qualified name has three parts:
9+
10+
1. The catalog - which is rarely used outside of Microsoft SQL Server
11+
1. The schema - which is often specified, but may be left blank if you are operating on tables in the default schema
12+
1. The name - which is required
13+
14+
Typical examples of fully qualified names are as follows:
15+
16+
- `"dbo..bar"` - a fully qualified name with a catalog (dbo) and a name (bar). This is typical for SQL Server
17+
- `"foo.bar"` - a fully qualified name with a schema (foo) and a name (bar). This is typical in many databases when you want to access tables that are not in the default schema
18+
- `"bar"` - a fully qualified name with just a name (bar). This will access a table or view in the default catalog and schema for a connection
19+
20+
21+
In MyBatis Dynamic SQL, the fully qualified name can be specified in different ways:
22+
23+
1. The fully qualified table name can be a constant String
24+
1. The fully qualified table name can be calculated at runtime based on a dynamic catalog and/or schema and a constant table name
25+
26+
### Constant Names
27+
28+
Constant names are used when you use the `SqlTable` constructor with a single String argument. For example:
29+
30+
```java
31+
public class MyTable extends SqlTable {
32+
public MyTable() {
33+
super("MyTable");
34+
}
35+
}
36+
```
37+
38+
Or
39+
40+
```java
41+
public class MyTable extends SqlTable {
42+
public MyTable() {
43+
super("MySchema.MyTable");
44+
}
45+
}
46+
```
47+
48+
49+
### Dynamic Names
50+
MyBatis Dynamic SQL allows you to dynamically specify a catalog and/or schema. This is useful for applications where the schema may change for different users or environments, or if you are using different schemas to shard the database. Dynamic names are used when you use a `SqlTable` constructor that accepts one or more `java.util.function.Supplier` arguments.
51+
52+
For example, suppose you wanted to change the schema based on the value of a system property. You could write a class like this:
53+
54+
```java
55+
public class SchemaSupplier {
56+
public static final String schema_property = "schemaToUse";
57+
58+
public static Optional<String> schemaPropertyReader() {
59+
return Optional.ofNullable(System.getProperty(schema_property));
60+
}
61+
}
62+
```
63+
64+
This class has a static method `schemaPropertyReader` that will return an `Optional<String>` containing the value of a system property. You could then reference this method in the constructor of the `SqlTable` like this:
65+
66+
```java
67+
public static final class User extends SqlTable {
68+
public User() {
69+
super(SchemaSupplier::schemaPropertyReader, "User");
70+
}
71+
}
72+
```
73+
74+
Whenever the table is referenced for rendering SQL, the name will be calculated based on the current value of the system property.
75+
76+
There are two constructors that can be used for dynamic names:
77+
78+
1. A constructor that accepts `Supplier<Optional<String>>` for the schema, and `String` for the name. This constructor assumes that the catalog is always empty or not used
79+
1. A constructor that accepts `Supplier<Optional<String>>` for the catalog, `Supplier<Optional<String>>` for the schema, and `String` for the name
80+
81+
If you are using Microsoft SQL Server and want to use a dynamic catalog name and ignore the schema, then you should use the second constructor like this:
82+
83+
```java
84+
public static final class User extends SqlTable {
85+
public User() {
86+
super(CatalogSupplier::catalogPropertyReader, Optional::empty, "User");
87+
}
88+
}
89+
```
90+
91+
The following table shows how the name is calculated in all combinations of suppliers:
92+
93+
Catalog Supplier Value | Schema Supplier Value | Name | Fully Qualified Name
94+
---|---|---|---
95+
"MyCatalog" | "MySchema" | "MyTable" | "MyCatalog.MySchema.MyTable"
96+
&lt;empty&gt; | "MySchema" | "MyTable" | "MySchema.MyTable"
97+
"MyCatalog" | &lt;empty&gt; | "MyTable" | "MyCatalog..MyTable"
98+
&lt;empty&gt; | &lt;empty&gt; | "MyTable" | "MyTable"
99+
100+
101+
102+
## Column Representation

0 commit comments

Comments
 (0)