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 ZONEdata type (also known as theDATETIMEOFFSETdata type); and SEQUENCEvalues using the newSEQUENCEsupport 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.

Glenn Paulley is a Director of Engineering at Sybase iAnywhere.

2 responses so far ↓
1 Sybase Blog: Glenn Paulley - Id Rather Play Golf // Sep 13, 2010 at 7:35 am
[...] replace them with more complete versions based on the dialects I’ve posted in this forum (see here and [...]
2 Sybase Blog: Glenn Paulley - Id Rather Play Golf // Aug 10, 2011 at 1:22 pm
[...] January 2010 I had previously posted an NHibernate dialect for SQL Anywhere 12.0.0 for the NHibernate 2.1.0 release. Version 3 of [...]