forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSequenceGenerator.cs
196 lines (177 loc) · 6.35 KB
/
SequenceGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
using System;
using System.Collections;
using NHibernate.Engine;
using NHibernate.Exceptions;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;
using NHibernate.Type;
using NHibernate.Util;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
namespace NHibernate.Id
{
/// <summary>
/// An <see cref="IIdentifierGenerator" /> that generates <c>Int64</c> values using an
/// oracle-style sequence. A higher performance algorithm is
/// <see cref="SequenceHiLoGenerator"/>.
/// </summary>
/// <remarks>
/// <p>
/// This id generation strategy is specified in the mapping file as
/// <code>
/// <generator class="sequence">
/// <param name="sequence">uid_sequence</param>
/// <param name="schema">db_schema</param>
/// </generator>
/// </code>
/// </p>
/// <p>
/// The <c>sequence</c> parameter is required while the <c>schema</c> is optional.
/// </p>
/// </remarks>
public partial class SequenceGenerator : IPersistentIdentifierGenerator, IConfigurable
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(SequenceGenerator));
/// <summary>
/// The name of the sequence parameter.
/// </summary>
public const string Sequence = "sequence";
/// <summary>
/// The parameters parameter, appended to the create sequence DDL.
/// For example (Oracle): <tt>INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE</tt>.
/// </summary>
public const string Parameters = "parameters";
private string sequenceName;
private IType identifierType;
private SqlString sql;
private string parameters;
public string SequenceName
{
get { return sequenceName; }
}
#region IConfigurable Members
/// <summary>
/// Configures the SequenceGenerator by reading the value of <c>sequence</c> and
/// <c>schema</c> from the <c>parms</c> parameter.
/// </summary>
/// <param name="type">The <see cref="IType"/> the identifier should be.</param>
/// <param name="parms">An <see cref="IDictionary"/> of Param values that are keyed by parameter name.</param>
/// <param name="dialect">The <see cref="Dialect.Dialect"/> to help with Configuration.</param>
public virtual void Configure(IType type, IDictionary<string, string> parms, Dialect.Dialect dialect)
{
var nativeSequenceName = PropertiesHelper.GetString(Sequence, parms, "hibernate_sequence");
bool needQuote = StringHelper.IsBackticksEnclosed(nativeSequenceName);
bool isQuelified = nativeSequenceName.IndexOf('.') > 0;
if (isQuelified)
{
string qualifier = StringHelper.Qualifier(nativeSequenceName);
nativeSequenceName = StringHelper.Unqualify(nativeSequenceName);
nativeSequenceName = StringHelper.PurgeBackticksEnclosing(nativeSequenceName);
sequenceName = qualifier + '.' + (needQuote ? dialect.QuoteForTableName(nativeSequenceName) : nativeSequenceName);
}
else
{
nativeSequenceName = StringHelper.PurgeBackticksEnclosing(nativeSequenceName);
sequenceName = needQuote ? dialect.QuoteForTableName(nativeSequenceName) : nativeSequenceName;
}
string schemaName;
string catalogName;
parms.TryGetValue(Parameters, out parameters);
parms.TryGetValue(PersistentIdGeneratorParmsNames.Schema, out schemaName);
parms.TryGetValue(PersistentIdGeneratorParmsNames.Catalog, out catalogName);
if (!isQuelified)
{
sequenceName = dialect.Qualify(catalogName, schemaName, sequenceName);
}
identifierType = type;
sql = new SqlString(dialect.GetSequenceNextValString(sequenceName));
}
#endregion
#region IIdentifierGenerator Members
/// <summary>
/// Generate an <see cref="Int16"/>, <see cref="Int32"/>, or <see cref="Int64"/>
/// for the identifier by using a database sequence.
/// </summary>
/// <param name="session">The <see cref="ISessionImplementor"/> this id is being generated in.</param>
/// <param name="obj">The entity for which the id is being generated.</param>
/// <returns>The new identifier as a <see cref="Int16"/>, <see cref="Int32"/>, or <see cref="Int64"/>.</returns>
public virtual object Generate(ISessionImplementor session, object obj)
{
try
{
var cmd = session.Batcher.PrepareCommand(CommandType.Text, sql, SqlTypeFactory.NoTypes);
DbDataReader reader = null;
try
{
reader = session.Batcher.ExecuteReader(cmd);
try
{
reader.Read();
object result = IdentifierGeneratorFactory.Get(reader, identifierType, session);
if (log.IsDebugEnabled())
{
log.Debug("Sequence identifier generated: {0}", result);
}
return result;
}
finally
{
reader.Close();
}
}
finally
{
session.Batcher.CloseCommand(cmd, reader);
}
}
catch (DbException sqle)
{
log.Error(sqle, "error generating sequence");
throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle, "could not get next sequence value");
}
}
#endregion
#region IPersistentIdentifierGenerator Members
/// <summary>
/// The SQL required to create the database objects for a SequenceGenerator.
/// </summary>
/// <param name="dialect">The <see cref="Dialect.Dialect"/> to help with creating the sql.</param>
/// <returns>
/// An array of <see cref="String"/> objects that contain the Dialect specific sql to
/// create the necessary database objects for the SequenceGenerator.
/// </returns>
public string[] SqlCreateStrings(Dialect.Dialect dialect)
{
string baseDDL = dialect.GetCreateSequenceString(sequenceName);
string paramsDDL = null;
if (parameters != null)
{
paramsDDL = ' ' + parameters;
}
return new string[] { string.Concat(baseDDL,paramsDDL) };
}
/// <summary>
/// The SQL required to remove the underlying database objects for a SequenceGenerator.
/// </summary>
/// <param name="dialect">The <see cref="Dialect.Dialect"/> to help with creating the sql.</param>
/// <returns>
/// A <see cref="String"/> that will drop the database objects for the SequenceGenerator.
/// </returns>
public string[] SqlDropString(Dialect.Dialect dialect)
{
return new string[] { dialect.GetDropSequenceString(sequenceName) };
}
/// <summary>
/// Return a key unique to the underlying database objects for a SequenceGenerator.
/// </summary>
/// <returns>
/// The configured sequence name.
/// </returns>
public string GeneratorKey()
{
return sequenceName;
}
#endregion
}
}