Sybase iAnywhere SQL AAnywhere Mobile and Embedded Database

I'd rather play golf


Thoughts on data management, autonomic computing, and self-managing database systems.

header image

New SQL Anywhere 12 NHibernate dialect

January 6th, 2010 · 2 Comments

You can find an NHibernate dialect customized for the Sybase SQL Anywhere 12 server (currently in beta) here.

This new SQLAnywhere12Dialect offers support for:

  • The TIMESTAMP WITH TIME ZONE data type (also known as the DATETIMEOFFSET data type); and
  • SEQUENCE values using the new SEQUENCE support in the SQL Anywhere 12 server.

SQL Anywhere 12 now supports ISO-ANSI SEQUENCE values in addition to AUTOINCREMENT (or IDENTITY) values. Sequences in a given database are stored in a new catalog table, SYS.SYSSEQUENCE, and SQL Anywhere 12 supports the .CURRVAL and .NEXTVAL syntax to permit applications to reference SEQUENCE values. Moreover, SQL Anywhere supports sequence_name.NEXTVAL in any context where an arbitrary expression could be used, making it straightforward to use a SEQUENCE as a default primary key value (see the example at line 4 below).

To illustrate SEQUENCE support in SQL Anywhere 12, I modified the “HelloNHibernate” application from the book [1] by Kuate, Harris, Bauer, and King. First, we had to create the SQL Anywhere 12 database:

1
dbinit helloseq.db

and then create a SEQUENCE object that would supply the primary key values for the Employee table:

2
CREATE SEQUENCE Employee_sequence INCREMENT BY 10 START WITH 1
3
4
5
6
7
CREATE TABLE Employee (
   ID INTEGER NOT NULL PRIMARY KEY DEFAULT (Employee_sequence.NEXTVAL),
   NAME VARCHAR(50) NULL,
   MANAGER INTEGER NULL
)

Note that the ID column of the Employee table no longer specifies AUTOINCREMENT; rather, its default value refers to the SEQUENCE named “Employee_sequence”. Using .NEXTVAL automatically provides the next sequence value to use when inserting a new Employee.

I created a new Visual Studio project, which I called NHibernate_sequences, to implement the HelloNHibernate test application with SEQUENCE support. The C# code in the HelloNHibernate application is unchanged from the book; the difference is in the NHibernate Employee.hbm.xml mapping file for the Employee class, which must be customized to declare the use of a SEQUENCE rather than AUTOINCREMENT:

8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="Windows-1252" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
  <class name="HelloNHibernate.Employee, HelloNHibernate_sequences" lazy="false">
    <id name="id" access="field">
      <generator class="sequence">
         <param name="sequence">Employee_sequence</param>
	 <param name="parameters">"INCREMENT BY 10 START WITH 1"</param>
      </generator>
    </id>
    <property name="name" access="field" column="name"/>
    <many-to-one access="field" name="manager" column="manager" cascade="all"/>
  </class>
</hibernate-mapping>

Rather than the generator class “native” (which implies AUTOINCREMENT), this version of Employee.hbm.xml uses a generator of type “SEQUENCE“, and it must specify the name of the SEQUENCE object from the catalog in order for NHibernate to generate the proper SELECT statements to the server.

In a nutshell, when using a SEQUENCE, a NHibernate application must first FETCH the value of sequence_name.NEXTVAL to enable the application to reference the object’s value. With AUTOINCREMENT, the procedure was reversed: the first step was to create the object, and subsequently the application issued SELECT @@IDENTITY to determine the value that was used for that AUTOINCREMENT column.

Note that with sequences it is possible to manage surrogate keys across one or more tables, since the space of values belongs to the SEQUENCE object and not to a specific table in the database.

With the HelloNHibernate application, I modified the Appinfo.Config file to reference the new SQLAnywhere12Dialect:

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="hibernate-configuration" 
             type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    <section name="log4net" 
             type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="proxyfactory.factory_class">
        NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
      </property>
      <property name="connection.provider">
        NHibernate.Connection.DriverConnectionProvider
      </property>
      <property name="connection.driver_class">
        NHibernate.Driver.ASA10ClientDriver
      </property>
      <property name="connection.connection_string">
        uid=dba;pwd=sql
      </property>
      <property name="show_sql">true</property>
      <property name="dialect">NHibernate.Dialect.SQLAnywhere12Dialect</property>
    </session-factory>
  </hibernate-configuration>
 
  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender, log4net">
      <layout type="log4net.Layout.PatternLayout, log4net">
        <param name="ConversionPattern" value="%d %p %m%n" />
      </layout>
    </appender>
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender,log4net" >
      <param name="File" value="c:\nh210\HelloNHibernate_sequences\log.txt" />
      <param name="AppendToFile" value="true" />
      <param name="DatePattern" value="yyyy.MM.dd" />
      <layout type="log4net.Layout.PatternLayout,log4net">
        <conversionPattern value="%d %p %m%n" />
      </layout>
    </appender>
    <root>
      <priority value="DEBUG" />
      <appender-ref ref="ConsoleAppender" />
    </root>
  </log4net>
</configuration>

Below is the console log output from executing the application with the latest Beta build of SQL Anywhere 12:

68
69
70
71
72
73
74
75
76
77
78
79
80
81
NHibernate: SELECT Employee_sequence.NEXTVAL FROM SYS.DUMMY
NHibernate: INSERT INTO Employee (name, manager, id) VALUES (?, ?, ?);p0 = 'Tobin Harris', p1 = NULL, p2 = 41
Saved Tobin to the database
NHibernate: select employee0_.id as id0_, employee0_.name as name0_, employee0_.manager as manager0_ from Employee employee0_ where employee0_.name='Tobin Harris'
NHibernate: SELECT Employee_sequence.NEXTVAL FROM SYS.DUMMY
NHibernate: INSERT INTO Employee (name, manager, id) VALUES (?, ?, ?);p0 = 'Pierre Henri Kuate', p1 = NULL, p2 = 51
NHibernate: UPDATE Employee SET name = ?, manager = ? WHERE id = ?;p0 = 'Tobin David Harris', p1 = 51, p2 = 41
Updated Tobin and added Pierre Henri
NHibernate: select employee0_.id as id0_, employee0_.name as name0_, employee0_.manager as manager0_ from Employee employee0_ order by employee0_.name asc
 
2 employees found:
'Hello World!', said Pierre Henri Kuate.
'Hello World!', said Tobin David Harris.
Press any key to exit...

The SQLAnywhere12Dialect is a derived class of the SQLAnywhere11Dialect class. Here is the source code:

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using System.Text.RegularExpressions;
 
using NHibernate.Dialect.Function;
using NHibernate.Dialect.Lock;
using NHibernate.Dialect.Schema;
using NHibernate.Engine;
using NHibernate.Exceptions;
using NHibernate.Mapping;
using NHibernate.SqlCommand;
using NHibernate.Type;
using NHibernate.Util;
 
using Environment = NHibernate.Cfg.Environment;
 
namespace NHibernate.Dialect
{
	/// <summary>
	/// SQL Dialect for SQL Anywhere 12 - for the NHibernate 2.0.1 distribution
	/// Copyright (C) 2010 Glenn Paulley
        /// Contact: http://iablog.sybase.com/paulley
	///
	/// This NHibernate dialect should be considered BETA software.
	///
	/// This library is free software; you can redistribute it and/or
	/// modify it under the terms of the GNU Lesser General Public
	/// License as published by the Free Software Foundation; either
	/// version 2.1 of the License, or (at your option) any later version.
	///
	/// This library is distributed in the hope that it will be useful,
	/// but WITHOUT ANY WARRANTY; without even the implied warranty of
	/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	/// Lesser General Public License for more details.
	///
	/// You should have received a copy of the GNU Lesser General Public
	/// License along with this library; if not, write to the Free Software
	/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	///
	/// </summary>
	/// <remarks>
	/// The SQLAnywhere12Dialect uses the SQLAnywhere11Dialect as its base class. 
        /// SQLAnywhere12Dialect includes support for ORACLE-style sequences, defined 
        /// in a new catalog table <tt>SYSSEQUENCE</tt>. 
	/// </remarks>
	public class SQLAnywhere12Dialect : SQLAnywhere11Dialect
	{
		/// <summary></summary>
		public SQLAnywhere12Dialect()
		{
		    RegisterDateTimeTypeMappings();
		    RegisterKeywords();
		}
 
		new protected void RegisterKeywords() 
		{
		    RegisterKeyword( "NEAR" );
		    RegisterKeyword( "LIMIT" );
		    RegisterKeyword( "OFFSET" );
		}
 
		new protected void RegisterDateTimeTypeMappings() 
		{
		    RegisterColumnType(DbType.DateTimeOffset, "DATETIMEOFFSET");
		}
 
 
		// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
		/// <summary> 
		/// SQL Anywhere supports <tt>SEQUENCES</tt> using a primarily SQL Standard 
	        /// syntax. Sequence values can be queried using the <tt>.CURRVAL</tt> identifier, and the next
	        /// value in a sequence can be retrieved using the <tt>.NEXTVAL</tt> identifier. Sequences
	        /// are retained in the SYS.SYSSEQUENCE catalog table. 
		/// </summary>
		public override bool SupportsSequences
		{
			get { return true; }
		}
 
	        /// <summary>
	        /// Pooled sequences does not refer to the CACHE parameter of the <tt>CREATE SEQUENCE</tt>
	        /// statement, but merely if the DBMS supports sequences that can be incremented or decremented
	        /// by values greater than 1. 
	        /// </summary>
		public override bool SupportsPooledSequences
		{
			get { return true; }
		}
 
		/// <summary> Get the <tt>SELECT</tt> command used retrieve the names of all sequences.</summary>
		/// <returns> The <tt>SELECT</tt> command; or NULL if sequences are not supported. </returns>
		public override string QuerySequencesString
		{
			get { return "SELECT SEQUENCE_NAME FROM SYS.SYSSEQUENCE"; }
		}
 
 
		public override string GetSequenceNextValString( string sequenceName )
		{
			return "SELECT " + GetSelectSequenceNextValString(sequenceName) + " FROM SYS.DUMMY";
		}
 
		public override string GetSelectSequenceNextValString( string sequenceName )
		{
			return sequenceName + ".NEXTVAL";
		}
 
		public override string GetCreateSequenceString( string sequenceName )
		{
			return "CREATE SEQUENCE " + sequenceName; // by default, is START WITH 1 MAXVALUE 2**63-1
		}
 
		public override string GetDropSequenceString( string sequenceName )
		{
			return "DROP SEQUENCE " + sequenceName;
		}
 
	}
}

One more detail: I’ve modified SQLAnywhere11Dialect.cs as well, to use a new schema metadata class which I’ve named SQLAnywhere11Metadata.cs. A customer (Paul Gibson) had indicated that one of the metadata calls in the SybaseAnywhereMetaData class, GetReservedWords(), was not implemented properly the in NHibernate 2.1.0GA distribution. I’ve fixed that, and decided to version the metadata class file in anticipation of additional ADO.NET metadata enhancements that may be forthcoming in a future SQL Anywhere release.

[1] Pierre Henri Kuate, Tobin Harris, Christian Bauer and Gavin King (2009). NHibernate in Action. Manning Publications, Greenwich, CT. ISBN 978-1-932394-92-4.

Tags: Database interfaces and persistent objects · NHibernate · SQL Anywhere

2 responses so far ↓