Database Object Representation
MyBatis Dynamic SQL works with Java objects that represent relational tables or views.
Table or View Representation
The class org.mybatis.dynamic.sql.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. A subclass of SqlTable
-
AliasableSqlTable
should be used in cases where you want to specify a table alias that should be used in all cases,
or if you need to change the table name at runtime.
A table or view name in SQL has three parts:
- The catalog - which is optional and is rarely used outside of Microsoft SQL Server. If unspecified the default catalog will be used - and many databases only have one catalog
- The schema - which is optional but is very often specified. If unspecified, the default schema will be used
- The table name - which is required
Typical examples of names are as follows:
"dbo..bar"
- a name with a catalog (dbo) and a table name (bar). This is typical for SQL Server"foo.bar"
- a name with a schema (foo) and a table name (bar). This is typical in many databases when you want to access tables that are not in the default schema"bar"
- a name with just a table name (bar). This will access a table or view in the default catalog and schema for a connection
In MyBatis Dynamic SQL, the full name of the table should be supplied on the constructor of the table object.
If a table name needs to change at runtime (say for sharding support), then use the withName
method on
AliasableSqlTable
to create an instance with the new name.
We recommend using the base class AliasableSqlTable
in all cases as it provides the most flexibility. The
SqlTable
class remains in the library for compatibility with older code only.
For example:
import org.mybatis.dynamic.sql.AliasableSqlTable;
public class MyTable extends AliasableSqlTable<MyTable> {
public MyTable() {
super("MyTable", MyTable::new);
}
}
Or
public class MyTable extends AliasableSqlTable<MyTable> {
public MyTable() {
super("MySchema.MyTable", MyTable::new);
}
}
You can change a table name:
public class MyTable extends AliasableSqlTable<MyTable> {
public MyTable() {
super("Schema1.MyTable", MyTable::new);
}
}
MyTable schema1Table = new MyTable();
MyTable schema2Table = schema1Table.withName("Schema2.MyTable");
Aliased Tables
In join queries, it is usually a good practice to specify table aliases. The select
statement includes
support for specifying table aliases in each query in a way that looks like natural SQL. For example:
SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
.from(orderMaster, "om")
.join(orderLine, "ol").on(orderMaster.orderId, equalTo(orderLine.orderId))
.join(itemMaster, "im").on(orderLine.itemId, equalTo(itemMaster.itemId))
.where(orderMaster.orderId, isEqualTo(2))
.build()
.render(RenderingStrategies.MYBATIS3);
In a query like this, the library will automatically append the table alias to the column name when the query is rendered. Internally, the alias for a column is determined by looking up the associated table in a HashMap maintained within the query model. If you do not specify a table alias, the library will automatically append the table name in join queries.
Unfortunately, this strategy fails for self-joins. It can also get confusing when there are sub-queries. Imagine a query like this:
SelectStatementProvider selectStatement = select(user.userId, user.userName, user.parentId)
.from(user, "u1")
.join(user, "u2").on(user.userId, equalTo(user.parentId))
.where(user.userId, isEqualTo(4))
.build()
.render(RenderingStrategies.MYBATIS3);
In this query it is not clear which instance of the user
table is used for each column, and there will only be entry in the
HashMap for the user
table - so only one of the aliases specified in the select statement will be in effect.
There are two ways to deal with this problem.
The first is to simply create another instance of the User SqlTable
object. With this method it is very clear which column
belongs to which instance of the table and the library can easily calculate aliases:
User user1 = new User();
User user2 = new User();
SelectStatementProvider selectStatement = select(user1.userId, user1.userName, user1.parentId)
.from(user1, "u1")
.join(user2, "u2").on(user1.userId, equalTo(user2.parentId))
.where(user2.userId, isEqualTo(4))
.build()
.render(RenderingStrategies.MYBATIS3);
Starting with version 1.3.1, there is new method where the alias can be specified in the table object itself. This allows
you to move the aliases out of the select
statement.
User user1 = user.withAlias("u1");
User user2 = user.withAlias("u2");
SelectStatementProvider selectStatement = select(user1.userId, user1.userName, user1.parentId)
.from(user1)
.join(user2).on(user1.userId, equalTo(user2.parentId))
.where(user2.userId, isEqualTo(4))
.build()
.render(RenderingStrategies.MYBATIS3);
To enable this support, your table objects should extend org.mybatis.dynamic.sql.AliasableSqlTable
rather than
org.mybatis.dynamic.sql.SqlTable
as follows:
public static final class User extends AliasableSqlTable<User> {
public final SqlColumn<Integer> userId = column("user_id", JDBCType.INTEGER);
public final SqlColumn<String> userName = column("user_name", JDBCType.VARCHAR);
public final SqlColumn<Integer> parentId = column("parent_id", JDBCType.INTEGER);
public User() {
super("User", User::new);
}
}
If you use an aliased table object, and also specify an alias in the select
statement, the alias from the select
statement will override the alias in the table object.
Column Representation
The class org.mybatis.dynamic.sql.SqlColumn
is used to represent a column in a table or view. An SqlColumn
is always
associated with a SqlTable
. In its most basic form, the SqlColumn
class holds a name and a reference to the
SqlTable
it is associated with. The table reference is required so that table aliases can be applied to columns in the
rendering phase.
The SqlColumn
will be rendered in SQL based on the RenderingStrategy
applied to the SQL statement. Typically the
rendering strategy generates a string that represents a parameter marker in whatever SQL engine you are using. For
example, MyBatis3 parameter markers are formatted as “#{some_attribute}”. By default, all columns are rendered with the
same strategy. The library supplies rendering strategies that are appropriate for several SQL execution engines
including MyBatis3 and Spring JDBC template.
In some cases it is necessary to override the rendering strategy for a particular column - so the SqlColumn
class
supports specifying a rendering strategy for a column that will override the rendering strategy applied to a statement.
A good example of this use case is with PostgreSQL. In that database it is required to add the string “::jsonb” to a
prepared statement parameter marker when inserting or updating JSON fields, but not for other fields. A column based
rendering strategy enables this.
The SqlColumn
class has additional optional attributes that are useful for SQL rendering - especially in MyBatis3.
These include:
- The
java.sql.JDBCType
of the column. This will be rendered into the MyBatis3 compatible parameter marker - which helps with picking type handlers and also inserting or updating null capable fields - A String containing a type handler - either a type handler alias or the fully qualified type of a type handler. This will be rendered into the MyBatis3 compatible parameter marker
If you are not using MyBatis3, then you do not need to specify the JDBC Type or type handler as those attributes are ignored by other rendering strategies.
Finally, the SqlColumn
class has methods to designate a column alias or sort order for use in different SQL
statements.
We recommend a usage pattern for creating table and column objects that provides quite a bit of flexibility for usage. See the Quick Start page for a complete example.