[#1547] Support "optimistic locking" in UpdatableRecord.store() and

delete()
-
Implemented optimistic locking using a Settings property, rather than an
additional API method
This commit is contained in:
Lukas Eder 2012-07-22 17:19:30 +02:00
parent 6cabc3e762
commit a737b76efb
7 changed files with 281 additions and 184 deletions

View File

@ -61,9 +61,11 @@ import org.jooq.TableRecord;
import org.jooq.UDTRecord;
import org.jooq.UpdatableRecord;
import org.jooq.UpdatableTable;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataChangedException;
import org.jooq.exception.InvalidResultException;
import org.jooq.impl.Factory;
import org.jooq.test.BaseTest;
import org.jooq.test.jOOQAbstractTest;
@ -590,80 +592,82 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658,
private <R extends UpdatableRecord<R>> void testStoreLocked0(
UpdatableTable<R> table, TableField<R, Integer> id, TableField<R, String> string) throws Exception {
Factory create = create(new Settings().withExecuteWithOptimisticLocking(true));
// Storing without changing shouldn't execute any queries
R record1 = create().fetchOne(table, id.equal(1));
assertEquals(0, record1.storeLocked());
assertEquals(0, record1.storeLocked());
R record1 = create.fetchOne(table, id.equal(1));
assertEquals(0, record1.store());
assertEquals(0, record1.store());
// Succeed if there are no concurrency issues
record1.setValue(string, "New Title 1");
assertEquals(1, record1.storeLocked());
assertEquals("New Title 1", create().fetchOne(table, id.equal(1)).getValue(string));
assertEquals(1, record1.store());
assertEquals("New Title 1", create.fetchOne(table, id.equal(1)).getValue(string));
// Get new books
R record2 = create().fetchOne(table, id.equal(1));
R record3 = create().fetchOne(table, id.equal(1));
R record2 = create.fetchOne(table, id.equal(1));
R record3 = create.fetchOne(table, id.equal(1));
// Still won't fail, but this will cause record3 to be stale
record2.setValue(string, "New Title 2");
assertEquals(1, record2.storeLocked());
assertEquals("New Title 2", create().fetchOne(table, id.equal(1)).getValue(string));
assertEquals(1, record2.store());
assertEquals("New Title 2", create.fetchOne(table, id.equal(1)).getValue(string));
// Storing without changing shouldn't execute any queries
assertEquals(0, record3.storeLocked());
assertEquals(0, record3.store());
// This should fail as record3 is stale
record3.setValue(string, "New Title 3");
try {
record3.storeLocked();
record3.store();
fail();
}
catch (DataChangedException expected) {}
assertEquals("New Title 2", create().fetchOne(table, id.equal(1)).getValue(string));
assertEquals("New Title 2", create.fetchOne(table, id.equal(1)).getValue(string));
// Refreshing first will work, though
record3.refresh();
record3.setValue(string, "New Title 3");
assertEquals(1, record3.storeLocked());
assertEquals("New Title 3", create().fetchOne(table, id.equal(1)).getValue(string));
assertEquals(1, record3.store());
assertEquals("New Title 3", create.fetchOne(table, id.equal(1)).getValue(string));
// Get new books
R record4 = create().fetchOne(table, id.equal(1));
R record5 = create().fetchOne(table, id.equal(1));
R record4 = create.fetchOne(table, id.equal(1));
R record5 = create.fetchOne(table, id.equal(1));
// Delete the book
assertEquals(1, record4.deleteLocked());
assertEquals(1, record4.delete());
// Storing without changing shouldn't execute any queries
assertEquals(0, record5.storeLocked());
assertEquals(0, record5.store());
// This should fail, as the database record no longer exists
record5.setValue(string, "New Title 5");
try {
record5.storeLocked();
record5.store();
fail();
}
catch (DataChangedException expected) {}
// Restore the book, then it should work
assertEquals(1, record4.storeLocked());
assertEquals(1, record5.storeLocked());
assertEquals("New Title 5", create().fetchOne(table, id.equal(1)).getValue(string));
assertEquals(1, record4.store());
assertEquals(1, record5.store());
assertEquals("New Title 5", create.fetchOne(table, id.equal(1)).getValue(string));
// Deleting the original should no longer be possible
try {
record4.deleteLocked();
record4.delete();
fail();
}
catch (DataChangedException expected) {}
// Refreshing and deleting should work
record4.refresh();
assertEquals(1, record4.deleteLocked());
assertEquals(1, record4.delete());
// Now the other record cannot be deleted anymore
try {
record5.deleteLocked();
record5.delete();
fail();
}
catch (DataChangedException expected) {}

View File

@ -9,7 +9,7 @@
<artifactId>jooq-parent</artifactId>
<version>2.5.0-SNAPSHOT</version>
</parent>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<name>jOOQ</name>
@ -38,7 +38,7 @@
<strict>false</strict>
<schemaDirectory>src/main/resources/xsd</schemaDirectory>
<schemaIncludes>
<include>jooq-runtime-2.3.0.xsd</include>
<include>jooq-runtime-2.5.0.xsd</include>
</schemaIncludes>
<generatePackage>org.jooq.conf</generatePackage>
<args>

View File

@ -38,6 +38,7 @@ package org.jooq;
import java.sql.ResultSet;
import java.sql.Statement;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataChangedException;
@ -71,6 +72,25 @@ public interface TableRecord<R extends TableRecord<R>> extends Record {
* record.</li>
* <li>If this record was loaded by jOOQ, and the provided keys' value was
* not changed, an <code>UPDATE</code> statement is executed.</li>
* <li>If an <code>UPDATE</code> statement is executed and
* {@link Settings#isExecuteWithOptimisticLocking()} is set to
* <code>true</code>, then this record will first be compared with the
* latest state in the database. In order to compare this record with the
* latest state, the database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
* <ul>
* <li> {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.</li>
* <li> {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual <code>UPDATE</code></li>
* </ul>
* <p>
* See {@link LockProvider#setForUpdate(boolean)} for more details</li>
* </ul>
* <p>
* In either statement, only those fields are inserted/updated, which had
@ -106,49 +126,10 @@ public interface TableRecord<R extends TableRecord<R>> extends Record {
* <code>WHERE</code> clause.
* @return The number of stored records.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
*/
int storeUsing(TableField<R, ?>... keys) throws DataAccessException;
/**
* Store this record back to the database assuming an optimistic lock.
* <p>
* This performs the same action as {@link #storeUsing(TableField...)},
* except that if an <code>UPDATE</code> is performed, this record will
* first be compared with the latest state in the database.
* <p>
* Note that in order to compare this record with the latest state, the
* database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
* <ul>
* <li> {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.</li>
* <li> {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual <code>UPDATE</code></li>
* </ul>
* <p>
* See {@link LockProvider#setForUpdate(boolean)} for more details
* <p>
* Unlike {@link #storeUsing(TableField...)}, this will fail if several
* records are concerned.
*
* @param keys The key fields used for deciding whether to execute an
* <code>INSERT</code> or <code>UPDATE</code> statement. If an
* <code>UPDATE</code> statement is executed, they are also the
* key fields for the <code>UPDATE</code> statement's
* <code>WHERE</code> clause.
* @return The number of stored records.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException if the record has already been changed in
* the database
* @see #storeUsing(TableField...)
* @see LockProvider#setForUpdate(boolean)
*/
int storeLockedUsing(TableField<R, ?>... keys) throws DataAccessException, DataChangedException;
int storeUsing(TableField<R, ?>... keys) throws DataAccessException, DataChangedException;
/**
* Deletes this record from the database, based on the value of the provided
@ -157,23 +138,11 @@ public interface TableRecord<R extends TableRecord<R>> extends Record {
* The executed statement is <code><pre>
* DELETE FROM [table]
* WHERE [key fields = key values]</pre></code>
*
* @param keys The key fields for the <code>DELETE</code> statement's
* <code>WHERE</code> clause.
* @return The number of deleted records.
* @throws DataAccessException if something went wrong executing the query
*/
int deleteUsing(TableField<R, ?>... keys) throws DataAccessException;
/**
* Deletes this record from the database assuming an optimistic lock.
* <p>
* This performs the same action as {@link #deleteUsing(TableField...)},
* except that this record will first be compared with the latest state in
* the database.
* <p>
* Note that in order to compare this record with the latest state, the
* database record will be locked pessimistically using a
* If {@link Settings#isExecuteWithOptimisticLocking()} is set to
* <code>true</code>, then this record will first be compared with the
* latest state in the database. In order to compare this record with the
* latest state, the database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
@ -188,20 +157,15 @@ public interface TableRecord<R extends TableRecord<R>> extends Record {
* </ul>
* <p>
* See {@link LockProvider#setForUpdate(boolean)} for more details
* <p>
* Unlike {@link #deleteUsing(TableField...)}, this will fail if several
* records are concerned.
*
* @param keys The key fields for the <code>DELETE</code> statement's
* <code>WHERE</code> clause.
* @return The number of deleted records.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException if the record has already been changed in
* the database
* @see #deleteUsing(TableField...)
* @see LockProvider#setForUpdate(boolean)
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
*/
int deleteLockedUsing(TableField<R, ?>... keys) throws DataAccessException, DataChangedException;
int deleteUsing(TableField<R, ?>... keys) throws DataAccessException, DataChangedException;
/**
* Refresh this record from the database, based on the value of the provided

View File

@ -38,6 +38,7 @@ package org.jooq;
import java.sql.ResultSet;
import java.sql.Statement;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataChangedException;
@ -97,13 +98,33 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends Updatable
* record.</li>
* <li>If this record was loaded by jOOQ, and the primary key value was not
* changed, an <code>UPDATE</code> statement is executed.</li>
* <li>If an <code>UPDATE</code> statement is executed and
* {@link Settings#isExecuteWithOptimisticLocking()} is set to
* <code>true</code>, then this record will first be compared with the
* latest state in the database. In order to compare this record with the
* latest state, the database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
* <ul>
* <li> {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.</li>
* <li> {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual <code>UPDATE</code></li>
* </ul>
* <p>
* In either statement, only those fields are inserted/updated, which had
* been explicitly set by client code, in order to allow for
* <code>DEFAULT</code> values to be applied by the underlying RDBMS. If no
* fields were modified, neither an <code>UPDATE</code> nor an
* <code>INSERT</code> will be executed. Possible statements are
* See {@link LockProvider#setForUpdate(boolean)} for more details</li>
* </ul>
* <p>
* In either <code>INSERT</code> or <code>UPDATE</code> statement, only
* those fields are inserted/updated, which had been explicitly set by
* client code, in order to allow for <code>DEFAULT</code> values to be
* applied by the underlying RDBMS. If no fields were modified, neither an
* <code>UPDATE</code> nor an <code>INSERT</code> will be executed. Possible
* statements are
* <ul>
* <li>
* <code><pre>
@ -132,44 +153,11 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends Updatable
* @return <code>1</code> if the record was stored to the database. <code>0
* </code> if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #storeUsing(TableField...)
*/
int store() throws DataAccessException;
/**
* Store this record back to the database assuming an optimistic lock.
* <p>
* This performs the same action as {@link #store()}, except that if an
* <code>UPDATE</code> is performed, this record will first be compared with
* the latest state in the database.
* <p>
* Note that in order to compare this record with the latest state, the
* database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
* <ul>
* <li> {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.</li>
* <li> {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual <code>UPDATE</code></li>
* </ul>
* <p>
* See {@link LockProvider#setForUpdate(boolean)} for more details
*
* @return <code>1</code> if the record was stored to the database. <code>0
* </code> if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException if the record has already been
* changed/deleted in the database
* @see #store()
* @see #storeLockedUsing(TableField...)
* @see LockProvider#setForUpdate(boolean)
*/
int storeLocked() throws DataAccessException, DataChangedException;
int store() throws DataAccessException, DataChangedException;
/**
* Deletes this record from the database, based on the value of the primary
@ -179,24 +167,10 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends Updatable
* DELETE FROM [table]
* WHERE [main key fields = main key values]</pre></code>
* <p>
* This is in fact the same as calling
* <code>delete(getTable().getMainKey().getFieldsArray())</code>
*
* @return <code>1</code> if the record was deleted from the database.
* <code>0</code> if deletion was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @see #deleteUsing(TableField...)
*/
int delete() throws DataAccessException;
/**
* Deletes this record from the database assuming an optimistic lock.
* <p>
* This performs the same action as {@link #delete()}, except that this
* record will first be compared with the latest state in the database.
* <p>
* Note that in order to compare this record with the latest state, the
* database record will be locked pessimistically using a
* If {@link Settings#isExecuteWithOptimisticLocking()} is set to
* <code>true</code>, then this record will first be compared with the
* latest state in the database. In order to compare this record with the
* latest state, the database record will be locked pessimistically using a
* <code>SELECT .. FOR UPDATE</code> statement. Not all databases support
* the <code>FOR UPDATE</code> clause natively. Namely, the following
* databases will show slightly different behaviour:
@ -211,17 +185,18 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends Updatable
* </ul>
* <p>
* See {@link LockProvider#setForUpdate(boolean)} for more details
* <p>
* This is in fact the same as calling
* <code>delete(getTable().getMainKey().getFieldsArray())</code>
*
* @return <code>1</code> if the record was deleted from the database.
* <code>0</code> if deletion was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException if the record has already been
* changed/deleted in the database
* @see #delete()
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #deleteUsing(TableField...)
* @see LockProvider#setForUpdate(boolean)
*/
int deleteLocked() throws DataAccessException, DataChangedException;
int delete() throws DataAccessException, DataChangedException;
/**
* Refresh this record from the database, based on the value of the primary

View File

@ -42,6 +42,7 @@ import java.util.Collection;
import java.util.LinkedHashSet;
import org.jooq.ConditionProvider;
import org.jooq.Configuration;
import org.jooq.DeleteQuery;
import org.jooq.Field;
import org.jooq.Identity;
@ -95,15 +96,7 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractRecord im
@Override
public final int storeUsing(TableField<R, ?>... keys) {
return storeUsing0(keys, false);
}
@Override
public final int storeLockedUsing(TableField<R, ?>... keys) {
return storeUsing0(keys, true);
}
private final int storeUsing0(TableField<R, ?>[] keys, boolean checkIfChanged) {
boolean checkIfChanged = isExecuteWithOptimisticLocking();
boolean executeUpdate = false;
for (TableField<R, ?> field : keys) {
@ -133,6 +126,16 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractRecord im
return result;
}
private final boolean isExecuteWithOptimisticLocking() {
Configuration configuration = getConfiguration();
if (configuration != null) {
return TRUE.equals(configuration.getSettings().isExecuteWithOptimisticLocking());
}
return false;
}
@SuppressWarnings("unchecked")
private final int storeInsert() {
Factory create = create();
@ -239,15 +242,8 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractRecord im
@Override
public final int deleteUsing(TableField<R, ?>... keys) {
return deleteUsing0(keys, false);
}
boolean checkIfChanged = isExecuteWithOptimisticLocking();
@Override
public final int deleteLockedUsing(TableField<R, ?>... keys) {
return deleteUsing0(keys, true);
}
private int deleteUsing0(TableField<R, ?>[] keys, boolean checkIfChanged) {
try {
DeleteQuery<R> delete = create().deleteQuery(getTable());

View File

@ -76,21 +76,11 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
return storeUsing(getMainKey().getFieldsArray());
}
@Override
public final int storeLocked() {
return storeLockedUsing(getMainKey().getFieldsArray());
}
@Override
public final int delete() {
return deleteUsing(getMainKey().getFieldsArray());
}
@Override
public final int deleteLocked() {
return deleteLockedUsing(getMainKey().getFieldsArray());
}
@Override
public final void refresh() {
refreshUsing(getMainKey().getFieldsArray());

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:jooq-runtime="http://www.jooq.org/xsd/jooq-runtime-2.3.0.xsd"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:version="1.0" jaxb:extensionBindingPrefixes="xjc"
targetNamespace="http://www.jooq.org/xsd/jooq-runtime-2.3.0.xsd">
<annotation>
<appinfo>
<jaxb:globalBindings>
<xjc:serializable uid="205"/>
</jaxb:globalBindings>
</appinfo>
</annotation>
<element name="settings" type="jooq-runtime:Settings"/>
<complexType name="Settings">
<all>
<!-- Whether any schema name should be rendered at all.
Use this for single-schema environments, or when all objects are made
available using synonyms -->
<element name="renderSchema" type="boolean" minOccurs="0" maxOccurs="1" default="true"/>
<!-- Configure render mapping for runtime schema / table rewriting in
generated SQL -->
<element name="renderMapping" type="jooq-runtime:RenderMapping" minOccurs="0" maxOccurs="1"/>
<!-- Whether rendered schema, table, column names, etc should be quoted
in rendered SQL, or transformed in any other way.
This is set to "QUOTED" by default for backwards-compatibility -->
<element name="renderNameStyle" type="jooq-runtime:RenderNameStyle" minOccurs="0" maxOccurs="1" default="QUOTED"/>
<!-- Whether SQL keywords should be rendered with upper or lower case -->
<element name="renderKeywordStyle" type="jooq-runtime:RenderKeywordStyle" minOccurs="0" maxOccurs="1" default="LOWER"/>
<!-- Whether rendered SQL should be pretty-printed -->
<element name="renderFormatted" type="boolean" minOccurs="0" maxOccurs="1" default="false"/>
<!-- The type of statement that is to be executed -->
<element name="statementType" type="jooq-runtime:StatementType" minOccurs="0" maxOccurs="1" default="PREPARED_STATEMENT"/>
<!-- When set to true, this will add jOOQ's default logging ExecuteListeners -->
<element name="executeLogging" type="boolean" minOccurs="0" maxOccurs="1" default="true"/>
<!-- The event listeners to be notified upon execution events -->
<element name="executeListeners" type="jooq-runtime:ExecuteListeners" minOccurs="0" maxOccurs="1"/>
<!-- Whether store() and delete() methods should be executed with optimistic locking -->
<element name="executeWithOptimisticLocking" type="boolean" minOccurs="0" maxOccurs="1" default="false"/>
</all>
</complexType>
<complexType name="RenderMapping">
<all>
<!-- The default schema as defined in org.jooq.Schema.getName()
This schema will be omitted in rendered SQL -->
<element name="defaultSchema" type="string" minOccurs="0" maxOccurs="1"/>
<!-- The mapped schemata configuration -->
<element name="schemata" type="jooq-runtime:MappedSchemata" minOccurs="0" maxOccurs="1"/>
</all>
</complexType>
<complexType name="MappedSchemata">
<sequence>
<element name="schema" type="jooq-runtime:MappedSchema" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="MappedSchema">
<all>
<!-- The input schema as defined in org.jooq.Schema.getName() -->
<element name="input" type="string" minOccurs="1" maxOccurs="1"/>
<!-- The output schema as it will be rendered in SQL
When this is omitted, you can still apply table mapping -->
<element name="output" type="string" minOccurs="0" maxOccurs="1"/>
<!-- Configure table mapping for runtime table rewriting in
generated SQL -->
<element name="tables" type="jooq-runtime:MappedTables" minOccurs="0" maxOccurs="1"/>
</all>
</complexType>
<complexType name="MappedTables">
<sequence>
<element name="table" type="jooq-runtime:MappedTable" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="MappedTable">
<all>
<!-- The input schema as defined in org.jooq.Table.getName() -->
<element name="input" type="string" minOccurs="1" maxOccurs="1"/>
<!-- The output schema as it will be rendered in SQL -->
<element name="output" type="string" minOccurs="1" maxOccurs="1"/>
</all>
</complexType>
<simpleType name="StatementType">
<restriction base="string">
<!-- Execute statements with inlined bind values, avoiding JDBC's
PreparedStatements -->
<enumeration value="STATIC_STATEMENT"/>
<!-- Execute statements with bind values, using JDBC's
PreparedStatements -->
<enumeration value="PREPARED_STATEMENT"/>
</restriction>
</simpleType>
<simpleType name="RenderNameStyle">
<restriction base="string">
<!-- Render object names quoted, as defined in the database. Use this
to stay on the safe side with case-sensitivity and special
characters. For instance:
Oracle : "SYS"."ALL_TAB_COLS"
MySQL : `information_schema`.`TABLES`
SQL Server: [INFORMATION_SCHEMA].[TABLES] -->
<enumeration value="QUOTED"/>
<!-- Render object names, as defined in the database. For instance:
Oracle : SYS.ALL_TAB_COLS
MySQL : information_schema.TABLES
SQL Server: INFORMATION_SCHEMA.TABLES -->
<enumeration value="AS_IS"/>
<!-- Force rendering object names in lower case. For instance:
Oracle : sys.all_tab_cols
MySQL : information_schema.tables
SQL Server: information_schema.tables -->
<enumeration value="LOWER"/>
<!-- Force rendering object names in upper case. For instance:
Oracle : SYS.ALL_TAB_COLS
MySQL : INFORMATION_SCHEMA.TABLES
SQL Server: INFORMATION_SCHEMA.TABLES -->
<enumeration value="UPPER"/>
</restriction>
</simpleType>
<simpleType name="RenderKeywordStyle">
<restriction base="string">
<!-- Keywords are rendered in lower case. For instance:
select .. from .. where .. -->
<enumeration value="LOWER"/>
<!-- Keywords are rendered in upper case. For instance:
SELECT .. FROM .. WHERE .. -->
<enumeration value="UPPER"/>
</restriction>
</simpleType>
<complexType name="ExecuteListeners">
<sequence>
<!-- An event listener implementing org.jooq.ExecuteListener -->
<element name="executeListener" type="string" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</schema>