Using NHibernate with SAP Advantage Database Server 12 - nhibernate

I'm currently trying to make NHibernate work with SAP's Advantage Database Server 12. Unfortunately, there is not much information around this database server in the internet, but in this case, I have to use it to access an existing database.
So I tried to set up a simple test project first which adds "Students" in a Student table, following this: https://www.tutorialspoint.com/nhibernate/nhibernate_basic_orm.htm
As for the ADS, I found a driver and dialect here: https://groups.google.com/forum/#!topic/nhibernate-development/s7HLs-aEXDk
I've set up a table and this code:
private void button1_Click(object sender, EventArgs e)
{
var cfg = new Configuration();
cfg.DataBaseIntegration(x =>
{
x.ConnectionString = "data source=C:\\Users\\xxx\\Downloads;ServerType=local; TableType=ADT; Shared=True; LockMode=COMPATIBLE";
x.Driver<AdvantageDatabaseServerDriver>();
x.Dialect<AdvantageDatabaseServer8Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession())
{
using (var tx = session.BeginTransaction())
{
//perform database logic
tx.Commit();
}
Console.ReadLine();
}
using (var session = sefact.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var student1 = new Students
{
ID = 1,
FirstMidName = "Allan",
LastName = "Bommer"
};
var student2 = new Students
{
ID = 2,
FirstMidName = "Jerry",
LastName = "Lewis"
};
session.Save(student1);
session.Save(student2);
tx.Commit();
}
Console.ReadLine();
}
}
But it keeps throwing this exception at the first write attempt:
NHibernate: INSERT INTO C:\Users\ad\Downloads\REFERENCE\Students.adt (LastName, FirstMidName) VALUES (?, ?); select NEWIDSTRING() from system.iota;p0 = 'Glenn' [Type: String (-1:0:0)], p1 = 'Allan' [Type: String (-1:0:0)]
Exception thrown: 'Advantage.Data.Provider.AdsException' in NHibernate.dll
Is there anything wrong with the dialect maybe? I really appreciate any help on this, thank you!

From the comments the problem is this:
NHibernate with the grammar you are using emits this code:
INSERT INTO
C:\Users\ad\Downloads\REFERENCE\Students.adt
(
LastName
, FirstMidName
)
VALUES
(
?
, ?
);
select NEWIDSTRING() from system.iota;
Apparently the ASP.NET driver for ADS can not handle this form of parameter passing correctly.
The knowledge base item you linked to suggest to change the SQL to use named parameters instead:
INSERT INTO
C:\Users\ad\Downloads\REFERENCE\Students.adt
(
LastName
, FirstMidName
)
VALUES
(
:p0
, :p1
);
select NEWIDSTRING() from system.iota;
I don't know enough of NHibernate to tell you how this can be achieved.

Related

inner-join query in Hibernate

I want to run a query with Hibernate, but I don't know what I'm donig wrong that I get errors.
I tried following
public List<String> findCourseForStudent(String pnr) {
factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "select Course.name from Course inner join CourseMaterial "
+ "on CourseMaterial.course_id = Course.id inner join CourseParticipantship "
+ "on CourseMaterial.id = CourseParticipantship.courseMaterial_id inner join Student "
+ "on Student.id=CourseParticipantship.student_id where Student.personalNumber='" + pnr + "'";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
query.setParameter("pnr", pnr);
List data = query.list();
tx.commit();
return data;
} catch(HibernateException e) {
if(tx != null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return null;
}
but I get the following error:
net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ]
I've searched for the error and tried some of the solutions, but none of them worked for me, so I tried to use HQL instead of using sql directly, but still no success :(
I don't actually know how to use INNER_JOIN in DetachedCriteria otherwise I think using DetachedCriteria I won't get any errors because I tried to use it for simpler queries and it worked fine.
Following is an example for a simple query that works fine, but I don't know how to wirte it for innerjoin queries
public List<Student> findForCourse(Integer integer) {
DetachedCriteria criteria = DetachedCriteria.forClass(Student.class);
criteria.add(Restrictions.eq("course.id", integer));
criteria.add(Restrictions.ne("active", Boolean.FALSE));
return getHibernateTemplate().findByCriteria(criteria);
}
Finally, I found out how to write my query in hibernate
public List<String> findCourseForStudent(String pnr) {
return jdbcTemplate.query(
"select Course.name from Course inner join CourseMaterial "
+ "on CourseMaterial.course_id = Course.id inner join CourseParticipantship "
+ "on CourseMaterial.id = CourseParticipantship.courseMaterial_id inner join thd.dbo.Student "
+ "on Student.id=CourseParticipantship.student_id where Student.personalNumber= ?", new Object[] { pnr }, new RowMapper() {
public Object mapRow(ResultSet rs, int row) throws SQLException {
return rs;
}
});
}

Combining two different jTables and adding button into jTable

I try to do for forum using java swing. Here are my codes for table :
public void SetUpJTable() {
DefaultTableModel tableModel = (DefaultTableModel) jTable.getModel();
String[] data = new String[4];
db.setUp("IT Innovation Project");
String sql = "Select topic_title,topic_description,topic_by from forumTopics WHERE topic_id = "
+ topicId + "";
ResultSet resultSet = null;
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("topic_title");
data[1] = resultSet.getString("topic_description");
data[2] = resultSet.getString("topic_by");
tableModel.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
I set up this table to retrieve the topic details which user select certain thread from main page. And I set up another table to store for the replies by users. Here is it :
public void SetUpJTableComment() {
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
String[] data = new String[3];
db.setUp("IT Innovation Project");
String sql = "Select reply_content,reply_by from forumReplies WHERE reply_topic = "
+ topicId + "";
ResultSet resultSet = null;
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("reply_content");
data[1] = resultSet.getString("reply_by");
tableModel1.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
And this is how I set up the table :
private JTable getJTableComment() {
String header[] = { "Comment", "Reply By" };
if (jTableComment == null) {
jTableComment = new JTable() {
public boolean isCellEditable(int nRow, int nCol) {
return false;
}
};
}
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
tableModel1.setColumnIdentifiers(header);
jTableComment.getColumnModel().getColumn(0).setMinWidth(700);
jTableComment.getColumnModel().getColumn(0).setMaxWidth(800);
jTableComment.getColumnModel().getColumn(1).setMinWidth(97);
jTableComment.getColumnModel().getColumn(1).setMaxWidth(100);
jTableComment.getTableHeader().setFont(
new Font("Dialog", Font.PLAIN, 20));
jTableComment.getTableHeader().setForeground(Color.white);
jTableComment.getTableHeader().setBackground(new Color(102, 102, 102));
jTableComment.setRowHeight(50);
jTableComment.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
jTableComment.setFont(new Font("Dialog", Font.PLAIN, 18));
return jTableComment;
}
It works perfectly with two separating tables. I wonder if there is some way to combine both of these tables into one table? And how can I customize the table to make it look less-liked a table because my current one is just .. solid-table and my teacher asked me to improve it but I have no idea to do so. And I tried to add button into the table but I realized that I cannot add it from the try statement because that is is retrieve data from database directly. Any guides? Thanks in advance.
You can use SQL join construct and have one table with more columns:
select topic_title,topic_description,topic_by,
reply_content,reply_by
from forumTopics join forumReplies
on (forumTopics.topic_id=forumReplies.topic_id) WHERE topic_id = 1234
then build the model from the five column result set as you are already doing.
But surely if there is more than one reply to a forum topic, the topic part will be repeated in the table.
To make a table not to look like a table, try JTreeTable from Swing Labs maybe, it allows to have tree-like subsections, exactly that is required. It is not part of the system library however, you will need to download it. Some source code on how to just JTreeTable can be found here.
On how JTreeTable looks is Swing Labs, you can see in they web-startable demo. It also shows the code sample automatically.

How to increment ID before any insert with NHibernate

It looks like NH gets MAX(ID) only once, at first insert and then stores this value internally, this causes me some problems when other processes inserts data. Then I have not actual ID and duplicate key exception is thrown.
Lets imagine we have table Cats
CREATE TABLE Cats(ID int, Name varchar(25))
Then we have corresponding mapping done with FluentNhibernate
public class CatMap : ClassMap<Cat>
{
public CatMap()
{
Id(m=>m.ID).GeneratedBy.Increment();
Map(m=>.Name);
}
}
All I want to achieve is to insert my Cat records with ID's generated by NHibernate using SELECT MAX(ID) FROM Cats before any insert. Executing Session.Flush after any commit dosnt work. I'v done some investigation using SQL Server profiler, and this sql stetement is executed only once (at first insert) - other inserts doesnt force to retreive actual MAX(ID). I know that other algorithms like HiLo are better, but I cant replace it.
As you found out, the NHibernate Increment id generator was not intended for use in a multi-user environment. You state that using a HiLo generator is not an option so you're left with these options:
use the Native generator and change the id column to use the database supported identity mechanism
use the Assigned generator and write code to determine the next valid id
create a Custom generator where you implement the IIdentifierGenerator interface to do what you need
Below is sample code for a custom generator that uses a generalized proc to get an ID for a given table. The main issue with this approach is that you must wrap the code in something like a Unit of Work pattern to ensure the 'select max(id) ..." and the insert are covered by the same database transaction. The IIdentifierGenerator link has the XML mapping you need to wire up this custom generator.
using System;
using System.Collections.Generic;
using System.Data;
using NHibernate.Dialect;
using NHibernate.Engine;
using NHibernate.Id;
using NHibernate.Persister.Entity;
using NHibernate.Type;
namespace YourCompany.Stuff
{
public class IdGenerator : IIdentifierGenerator, IConfigurable
{
private string _tableName;
// The "select max(id) ..." query will go into this proc:
private const string DefaultProcedureName = "dbo.getId";
public string ProcedureName { get; protected set; }
public string TableNameParameter { get; protected set; }
public string OutputParameter { get; protected set; }
public IdGenerator()
{
ProcedureName = DefaultProcedureName;
TableNameParameter = "#tableName";
OutputParameter = "#newID";
}
public object Generate(ISessionImplementor session, object obj)
{
int newId;
using (var command = session.Connection.CreateCommand())
{
var tableName = GetTableName(session, obj.GetType());
command.CommandType = CommandType.StoredProcedure;
command.CommandText = ProcedureName;
// Set input parameters
var parm = command.CreateParameter();
parm.Value = tableName;
parm.ParameterName = TableNameParameter;
parm.DbType = DbType.String;
command.Parameters.Add(parm);
// Set output parameter
var outputParameter = command.CreateParameter();
outputParameter.Direction = ParameterDirection.Output;
outputParameter.ParameterName = OutputParameter;
outputParameter.DbType = DbType.Int32;
command.Parameters.Add(outputParameter);
// Execute the stored procedure
command.ExecuteNonQuery();
var id = (IDbDataParameter)command.Parameters[OutputParameter];
newId = int.Parse(id.Value.ToString());
if (newId < 1)
throw new InvalidOperationException(
string.Format("Could not retrieve a new ID with proc {0} for table {1}",
ProcedureName,
tableName));
}
return newId;
}
public void Configure(IType type, IDictionary<string, string> parms, Dialect dialect)
{
_tableName = parms["TableName"];
}
private string GetTableName(ISessionImplementor session, Type objectType)
{
if (string.IsNullOrEmpty(_tableName))
{
//Not set by configuration, default to the mapped table of the actual type from runtime object:
var persister = (IJoinable)session.Factory.GetClassMetadata(objectType);
var qualifiedTableName = persister.TableName.Split('.');
_tableName = qualifiedTableName[qualifiedTableName.GetUpperBound(0)]; //Get last string
}
return _tableName;
}
}
}

EntityFramework, Insert if not exist, otherwise update

I'm having a Entity-Set Countries, reflecting a database table '<'char(2),char(3),nvarchar(50> in my database.
Im having a parser that returns a Country[] array of parsed countries, and is having issues with getting it updated in the right way. What i want is: Take the array of countries, for those countries not already in the database insert them, and those existing update if any fields is different. How can this be done?
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
//Code missing
}
}
I was thinking something like
for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields.
Hope anyone have had this issues before, and have a solution for me. If i cant use the Country object and insert/update an array of them easy, i dont see much benefict of using the framework, as from performers i think its faster to write a custom sql script that inserts them instead of ect checking if an country is already in the database before inserting?
Solution
See answer of post instead.
I added override equals to my country class:
public partial class Country
{
public override bool Equals(object obj)
{
if (obj is Country)
{
var country = obj as Country;
return this.CountryTreeLetter.Equals(country.CountryTreeLetter);
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = hash * 7 + (int)CountryTreeLetter[0];
hash = hash * 7 + (int)CountryTreeLetter[1];
hash = hash * 7 + (int)CountryTreeLetter[2];
return hash;
}
}
and then did:
var data = e.ParsedData as Country[];
using (var db = new entities())
{
foreach (var item in data.Except(db.Countries))
{
db.AddToCountries(item);
}
db.SaveChanges();
}
I would do it straightforward:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
foreach(var country in data)
{
var countryInDb = db.Countries
.Where(c => c.Name == country.Name) // or whatever your key is
.SingleOrDefault();
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
I don't know how often your application must run this or how many countries your world has. But I have the feeling that this is nothing where you must think about sophisticated performance optimizations.
Edit
Alternative approach which would issue only one query:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
var names = data.Select(c => c.Name);
var countriesInDb = db.Countries
.Where(c => names.Contains(c.Name))
.ToList(); // single DB query
foreach(var country in data)
{
var countryInDb = countriesInDb
.SingleOrDefault(c => c.Name == country.Name); // runs in memory
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
The modern form, using later EF versions would be:
context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added);
context.SaveChanges();
AlreadyExists can come from checking the key or by querying the database to see whether the item already exists there.
You can implement your own IEqualityComparer<Country> and pass that to the Except() method. Assuming your Country object has Id and Name properties, one example of that implementation could look like this:
public class CountryComparer : IEqualityComparer<Country>
{
public bool Equals(Country x, Country y)
{
return x.Name.Equals(y.Name) && (x.Id == y.Id);
}
public int GetHashCode(Country obj)
{
return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
}
}
and use it as
data.Countries.Except<Country>(db, new CountryComparer());
Although, in your case it looks like you just need to extract new objects, you can use var newCountries = data.Where(c => c.Id == Guid.Empty); if your Id is Guid.
The best way is to inspect the Country.EntityState property and take actions from there regarding on value (Detached, Modified, Added, etc.)
You need to provide more information on what your data collection contains i.e. are the Country objects retrieved from a database through the entityframework, in which case their context can be tracked, or are you generating them using some other way.
I am not sure this will be the best solution but I think you have to get all countries from DB then check it with your parsed data
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
List<Country> mycountries = db.Countries.ToList();
foreach(var PC in data)
{
if(mycountries.Any( C => C.Name==PC.Name ))
{
var country = mycountries.Any( C => C.Name==PC.Name );
//Update it here
}
else
{
var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters
newcountry.Name = PC.Name;
db.AddToCountries(newcountry)
}
}
db.SaveChanges();
}
}

get next sequence value from database using hibernate

I have an entity that has an NON-ID field that must be set from a sequence.
Currently, I fetch for the first value of the sequence, store it on the client's side, and compute from that value.
However, I'm looking for a "better" way of doing this. I have implemented a way to fetch the next sequence value:
public Long getNextKey()
{
Query query = session.createSQLQuery( "select nextval('mySequence')" );
Long key = ((BigInteger) query.uniqueResult()).longValue();
return key;
}
However, this way reduces the performance significantly (creation of ~5000 objects gets slowed down by a factor of 3 - from 5740ms to 13648ms ).
I have tried to add a "fake" entity:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
}
However this approach didn't work either (all the Ids returned were 0).
Can someone advise me how to fetch the next sequence value using Hibernate efficiently?
Edit: Upon investigation, I have discovered that calling Query query = session.createSQLQuery( "select nextval('mySequence')" ); is by far more inefficient than using the #GeneratedValue- because of Hibernate somehow manages to reduce the number of fetches when accessing the sequence described by #GeneratedValue.
For example, when I create 70,000 entities, (thus with 70,000 primary keys fetched from the same sequence), I get everything I need.
HOWEVER , Hibernate only issues 1404 select nextval ('local_key_sequence') commands. NOTE: On the database side, the caching is set to 1.
If I try to fetch all the data manually, it will take me 70,000 selects, thus a huge difference in performance. Does anyone know the internal functioning of Hibernate, and how to reproduce it manually?
You can use Hibernate Dialect API for Database independence as follow
class SequenceValueGetter {
private SessionFactory sessionFactory;
// For Hibernate 3
public Long getId(final String sequenceName) {
final List<Long> ids = new ArrayList<Long>(1);
sessionFactory.getCurrentSession().doWork(new Work() {
public void execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
ids.add(resultSet.getLong(1));
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
});
return ids.get(0);
}
// For Hibernate 4
public Long getID(final String sequenceName) {
ReturningWork<Long> maxReturningWork = new ReturningWork<Long>() {
#Override
public Long execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
return resultSet.getLong(1);
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
};
Long maxRecord = sessionFactory.getCurrentSession().doReturningWork(maxReturningWork);
return maxRecord;
}
}
Here is what worked for me (specific to Oracle, but using scalar seems to be the key)
Long getNext() {
Query query =
session.createSQLQuery("select MYSEQ.nextval as num from dual")
.addScalar("num", StandardBasicTypes.BIG_INTEGER);
return ((BigInteger) query.uniqueResult()).longValue();
}
Thanks to the posters here: springsource_forum
I found the solution:
public class DefaultPostgresKeyServer
{
private Session session;
private Iterator<BigInteger> iter;
private long batchSize;
public DefaultPostgresKeyServer (Session sess, long batchFetchSize)
{
this.session=sess;
batchSize = batchFetchSize;
iter = Collections.<BigInteger>emptyList().iterator();
}
#SuppressWarnings("unchecked")
public Long getNextKey()
{
if ( ! iter.hasNext() )
{
Query query = session.createSQLQuery( "SELECT nextval( 'mySchema.mySequence' ) FROM generate_series( 1, " + batchSize + " )" );
iter = (Iterator<BigInteger>) query.list().iterator();
}
return iter.next().longValue() ;
}
}
If you are using Oracle, consider specifying cache size for the sequence. If you are routinely create objects in batches of 5K, you can just set it to a 1000 or 5000. We did it for the sequence used for the surrogate primary key and were amazed that execution times for an ETL process hand-written in Java dropped in half.
I could not paste formatted code into comment. Here's the sequence DDL:
create sequence seq_mytable_sid
minvalue 1
maxvalue 999999999999999999999999999
increment by 1
start with 1
cache 1000
order
nocycle;
To get the new id, all you have to do is flush the entity manager. See getNext() method below:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
public static long getNext(EntityManager em) {
SequenceFetcher sf = new SequenceFetcher();
em.persist(sf);
em.flush();
return sf.getId();
}
}
POSTGRESQL
String psqlAutoincrementQuery = "SELECT NEXTVAL(CONCAT(:psqlTableName, '_id_seq')) as id";
Long psqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(psqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("psqlTableName", psqlTableName)
.uniqueResult();
MYSQL
String mysqlAutoincrementQuery = "SELECT AUTO_INCREMENT as id FROM information_schema.tables WHERE table_name = :mysqlTableName AND table_schema = DATABASE()";
Long mysqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(mysqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("mysqlTableName", mysqlTableName)
.uniqueResult();
Interesting it works for you. When I tried your solution an error came up, saying that "Type mismatch: cannot convert from SQLQuery to Query". --> Therefore my solution looks like:
SQLQuery query = session.createSQLQuery("select nextval('SEQUENCE_NAME')");
Long nextValue = ((BigInteger)query.uniqueResult()).longValue();
With that solution I didn't run into performance problems.
And don't forget to reset your value, if you just wanted to know for information purposes.
--nextValue;
query = session.createSQLQuery("select setval('SEQUENCE_NAME'," + nextValue + ")");
Spring 5 has some builtin helper classes for that:
org/springframework/jdbc/support/incrementer
Here is the way I do it:
#Entity
public class ServerInstanceSeq
{
#Id //mysql bigint(20)
#SequenceGenerator(name="ServerInstanceIdSeqName", sequenceName="ServerInstanceIdSeq", allocationSize=20)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ServerInstanceIdSeqName")
public Long id;
}
ServerInstanceSeq sis = new ServerInstanceSeq();
session.beginTransaction();
session.save(sis);
session.getTransaction().commit();
System.out.println("sis.id after save: "+sis.id);
Your idea with the SequenceGenerator fake entity is good.
#Id
#GenericGenerator(name = "my_seq", strategy = "sequence", parameters = {
#org.hibernate.annotations.Parameter(name = "sequence_name", value = "MY_CUSTOM_NAMED_SQN"),
})
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
It is important to use the parameter with the key name "sequence_name". Run a debugging session on the hibernate class SequenceStyleGenerator, the configure(...) method at the line final QualifiedName sequenceName = determineSequenceName( params, dialect, jdbcEnvironment ); to see more details about how the sequence name is computed by Hibernate. There are some defaults in there you could also use.
After the fake entity, I created a CrudRepository:
public interface SequenceRepository extends CrudRepository<SequenceGenerator, Long> {}
In the Junit, I call the save method of the SequenceRepository.
SequenceGenerator sequenceObject = new SequenceGenerator();
SequenceGenerator result = sequenceRepository.save(sequenceObject);
If there is a better way to do this (maybe support for a generator on any type of field instead of just Id), I would be more than happy to use it instead of this "trick".