I want to use mapping by code so I have a class Employee (namespace NHibernateTests.Classes) and a class EmployeeMappings (namespace NHibernateTests.Mappings)
My whole nhibernate configuration is set in an xml file hibernate.cfg.xml which currently goes like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.OracleClientDriver</property>
<property name="connection.connection_string">User Id=NHIBERNATE;Password=NHIBERNATE;Data Source=XE</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<mapping assembly="NHibernateTests"/>
</session-factory>
</hibernate-configuration>
Which gives me the runtime error : No persister for: NHibernateTests.Classes.Employee
I tried (and error) some setting for mapping element but with no luck. I read how to set ressource for hbm.xml elements but couldn't find an answer for by code mapping.
namespace NHibernateTests.Classes
{
public class Employee
{
public virtual Address Address { get; set; }
public virtual string FirstName { get; set; }
public virtual int Id { get; set; }
}
}
namespace NHibernateTests.Mappings
{
public class EmployeeMappings : ClassMapping<Employee>
{
public EmployeeMappings()
{
this.Id(e => e.Id, mapper =>
{
mapper.Generator(Generators.HighLow);
});
}
}
}
With a mapping by code you should configure also your factory by code. There is one of few how-to:
NHibernate 3.2 Mapping by Code – Basic Mapping
cited code snippets (see above link for more details)
private static Configuration ConfigureNHibernate()
{
var configure = new Configuration();
configure.SessionFactoryName("BuildIt");
configure.DataBaseIntegration(db =>
{
db.Dialect();
db.Driver();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionStringName = "NH3";
db.Timeout = 10;
// enabled for testing
db.LogFormattedSql = true;
db.LogSqlInConsole = true;
db.AutoCommentSql = true;
});
var mapping = GetMappings();
configure.AddDeserializedMapping(mapping, "NHSchemaTest");
SchemaMetadataUpdater.QuoteTableAndColumns(configure);
return configure;
}
thew way how to get HbmMapping
private static HbmMapping GetMappings()
{
var mapper = new ModelMapper();
mapper.AddMappings(Assembly.GetAssembly(typeof(ProvinceMap)).GetExportedTypes());
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
return mapping;
}
Related
I have created a small API based on NancyFx on .NET Core 2. It uses AutoFac as the IOC container and NHibernate 5.3 to access the database.
I have run into a problem with threading and the CurrentSessionContext. Basically when I enter the AfterRequest pipeline, I am usually on another thread, and then the CurrentSessionContext doesn't know about the binding I did at the beginning of the request.
I have tried to use the WebSessionContext instead, but since I am using the stack I am, there is no HttpContext.Current. To get access to the HttpContext you have to inject the Microsoft.AspNetCore.Http.IHttpContextAccessor where you need it.
How can I tell NHibernate to bind to my own context somehow, so I my session isn't lost between BeforeRequest and AfterRequest?
To make it easy to wrap my data accecss in a transaction I have added the following to my Nancy Bootstrapper:
protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
ConfigureNHibernateSessionPerRequest(container, pipelines);
}
private void ConfigureNHibernateSessionPerRequest(ILifetimeScope container, IPipelines pipelines)
{
pipelines.BeforeRequest += ctx => CreateSession(container);
pipelines.AfterRequest += ctx => CommitSession(container);
pipelines.OnError += (ctx, ex) => RollbackSession(container);
}
private Response CreateSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
var requestSession = sessionFactory.OpenSession();
CurrentSessionContext.Bind(requestSession);
requestSession.BeginTransaction();
return null;
}
private AfterPipeline CommitSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Commit();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
private Response RollbackSession(ILifetimeScope container)
{
var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
var sessionFactory = provider.Factory;
if (CurrentSessionContext.HasBind(sessionFactory))
{
var requestSession = sessionFactory.GetCurrentSession();
requestSession.Transaction.Rollback();
CurrentSessionContext.Unbind(sessionFactory);
requestSession.Dispose();
}
return null;
}
My hibernate.cfg.xml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">...</property>
<property name="show_sql">true</property>
<property name ="current_session_context_class">thread_static</property>
</session-factory>
</hibernate-configuration>
And I wire up the Sessionfactory like this:
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(Assembly.GetExecutingAssembly());
_factory = configuration.BuildSessionFactory();
You can try using AspNetCore middleware since that will give you access to HttpContext.
I found a solution that works, but isn't really as nice as I would have liked it to be.
In my Nancy Bootstrapper I have added a public static property that I populate from the ApplicationStartup hook:
public class Bootstrapper : AutofacNancyBootstrapper
{
public static IHttpContextAccessor HttpContextAccessor { get; private set; }
protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
{
HttpContextAccessor = container.Resolve<IHttpContextAccessor>();
}
}
Then I have created a new custom CurrentSessionContext that I just called CoreSessionContext. It extends the abstract MapBasedSessionContext just like the WebSessionContext does, and then I inject the HttpContextAccessor in the constructor by accessing the static property on the Bootstrapper.
public class CoreSessionContext : MapBasedSessionContext
{
private IHttpContextAccessor _httpContextAccessor;
private const string SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";
public CoreSessionContext(ISessionFactoryImplementor factory) : base(factory)
{
_httpContextAccessor = Bootstrapper.HttpContextAccessor;
}
protected override IDictionary GetMap()
{
return _httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] as IDictionary;
}
protected override void SetMap(IDictionary value)
{
_httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] = value;
}
}
The last thing I did was to remove the current_session_context_class element from the hibernate.cfg.xml file and then wire up the SessionFactory with my custom session context like this in line three:
var configuration = new Configuration();
configuration.Configure();
configuration.CurrentSessionContext<CoreSessionContext>();
configuration.AddAssembly(Assembly.GetExecutingAssembly());
_factory = configuration.BuildSessionFactory();
Then I am able to use the HttpContext provided by AspNetCore.
Not as pretty as I would like it to be, but it works.
Recently, I have been trying to implement an in-memory database based on HSQLDB for one of our applications which uses Oracle DB in the production. The application uses spring framework. However, I have to implement the data-source bean programmatically as we are using the existing SQL DDL statements(Oracle queries) and so have to programmatically remove constructs like namespaces before they can run on HSQLDB.
I initialize the database using EmbeddedDatabaseBuilder(ResourceLoader).
Now my issue is that I now want to add connection pooling using say c3p0 to this.
Normally I would be using
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="HSQLDB driver path" /> (this is just for representation)
<property name="jdbcUrl" value="${xxx.jdbcUrl}" />
<property name="user" value="${xxx.username}" />
<property name="password" value="${xxx.password}" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="3" />
<property name="maxIdleTime" value="20" />
</bean>
However, I am confused as to how I can define this while using the Spring embedded database.
Disclaimer: I am really new to spring.
Following this link:
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Utils {
public static ComboPooledDataSource newDefaultDS() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setMinPoolSize(1);
dataSource.setMaxPoolSize(1);
dataSource.setMaxIdleTime(20);
return dataSource;
}
}
import java.beans.PropertyVetoException;
import java.sql.Driver;
import org.springframework.jdbc.datasource.embedded.ConnectionProperties;
import org.springframework.jdbc.datasource.embedded.DataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class ComboPoolDataSourceFactory implements DataSourceFactory,
ConnectionProperties {
private final ComboPooledDataSource dataSource;
public ComboPoolDataSourceFactory() {
this(C3P0Utils.newDefaultDS());
}
public ComboPoolDataSourceFactory(ComboPooledDataSource dataSource) {
assert dataSource != null;
this.dataSource = dataSource;
}
public ConnectionProperties getConnectionProperties() {
return this;
}
public ComboPooledDataSource getDataSource() {
return dataSource;
}
public void setUsername(String username) {
dataSource.setUser(username);
}
public void setPassword(String password) {
dataSource.setPassword(password);
}
public void setUrl(String url) {
dataSource.setJdbcUrl(url);
}
public void setDriverClass(Class<? extends Driver> driverClass) {
try {
dataSource.setDriverClass(driverClass.getName());
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
}
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
public class EmbeddedC3P0DatabaseBuilder extends EmbeddedDatabaseBuilder {
public EmbeddedC3P0DatabaseBuilder() {
setDataSourceFactory(new ComboPoolDataSourceFactory());
}
}
And a short usage example:
EmbeddedC3P0DatabaseBuilder builder = new EmbeddedC3P0DatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.H2)
.addScript("setup-tables.sql")
.build();
JdbcTemplate template = new JdbcTemplate(db);
....
db.shutdown();
I wanted to test whether my entities can be persisted to the database or not, so I came across this article:
http://www.codethinked.com/nhibernate-20-sqlite-and-in-memory-databases
My code to initialize the session factory is the same the one in the article:
public class NHibernateInMemoryTestFixtureBase
{
protected static ISessionFactory sessionFactory;
protected static Configuration configuration;
public static void InitalizeSessionFactory(params Assembly[] assemblies)
{
if (sessionFactory != null)
return;
var properties = new Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SQLite20Driver");
properties.Add("dialect", "NHibernate.Dialect.SQLiteDialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
properties.Add("connection.connection_string", "Data Source=:memory:;Version=3;New=True;");
properties.Add("connection.release_mode", "on_close");
configuration = new Configuration();
configuration.Properties = properties;
foreach (Assembly assembly in assemblies)
{
configuration = configuration.AddAssembly(assembly);
}
sessionFactory = configuration.BuildSessionFactory();
}
public ISession CreateSession()
{
ISession openSession = sessionFactory.OpenSession();
IDbConnection connection = openSession.Connection;
new SchemaExport(configuration).Execute(false, true, false, true, connection, null);
return openSession;
}
}
And here's my test:
[Test]
public void IWillChangeThisNameLater()
{
InitalizeSessionFactory(typeof(LogRepository).Assembly);
var session = this.CreateSession();
Log log = Log.New("a", "b", "I");
session.Save(log);
session.Flush();
Assert.Greater(log.IDColumn, 0);
}
And the problem is, I removed the "a" property of Log from the log.hbm.xml and session.Save(log) is not throwing an exception or anything, it just works...
This must be obvious and on porpose, but I fail to find out why that is, how can it save it if is not mapped, is that how the in memory database work? how can I test my mapping then?
I mainly did this in-memory test so that I can know right away if a valid entity is failing to persist, of course that would include missing properties on the mapping file.
Any thoughts will be appreciated.
EDIT:
As requested,
the Log entity definition:
public class Log : DomainBase<Log, ILogRepository<Log>>
{
private int logId;
private string tableName;
private string field;
private string userLogin;
protected Log()
{
}
protected Log(string tableName, string field, string userLogin)
{
TableName = tableName;
Field = field;
UserLogin = userLogin;
}
public virtual int LogId { get; set; }
public virtual string TableName { get; set; }
public virtual string Field { get; set; }
public virtual string UserLogin { get; set; }
}
the Log Mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DomainProject" table="Log" lazy="true">
<id name="logId" column="ID" type="int">
<generator class="native" />
</id>
<property name="TableName" column="TableName" type="string" />
<property name="Field" column="Field" type="string" />
<property name="UserLogin" column="UserLogin" type="string" />
</class>
</hibernate-mapping>
If a class contains a property that is not mentioned in the mappings, NHibernate will ignore the property.
I'm following the NHibernate getting started tutorial: "Your first NHibernate based application". I'm at the point where I create a Product object then use Session.get() to prove I can read the object.
It works fine with SQL Server Ce, but I'm getting an exception when I try to use DB2. (The SQL Server Ce version works - that is. There are some minor changes between the versions like int instead of GUID for the Id.)
I'm very experienced with Hibernate and SQL databases. This is my first experience with NHibernate and DB2. (Coming from the Java world). I'd appreciate any suggestions, especially from the (evidently few) people who are using NHibernate on DB2.
Rob
The full exception is
Test method Examples.DB2.NHibernateExamples.Can_add_new_product threw
exception: NHibernate.Exceptions.GenericADOException: could not load
an entity: [Examples.DB2.Domain.Product#1][SQL: SELECT product0_.Id as
Id1_0_, product0_.Name as Name1_0_, product0_.Category as
Category1_0_, product0_.Discontinued as Disconti4_1_0_ FROM Product
product0_ WHERE product0_.Id=?] ---> System.IndexOutOfRangeException:
Invalid index 0 for this DB2ParameterCollection with Count=0.
This is happening in the Get(...) call in the following code:
[TestInitialize]
public void TestInitialize()
{
TestFixtureSetup();
SetupContext();
}
[TestMethod]
public void Can_add_new_product()
{
var product = new Product { Id = 1, Name = "Apple", Category = "Fruits"};
using (ISession session = _sessionFactory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(product);
transaction.Commit();
}
}
using (ISession session = _sessionFactory.OpenSession())
{
//var query = session.CreateQuery("from Product");
//var products = query.List<Product>();
//Assert.AreEqual(1, products.Count);
var fromDb = session.Get<Product>(product.Id);
Assert.IsNotNull(fromDb);
Assert.AreNotSame(product, fromDb);
Assert.AreEqual(product.Name, fromDb.Name);
Assert.AreEqual(product.Category, fromDb.Category);
}
}
private void TestFixtureSetup()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof (Domain.Product).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
}
private void SetupContext()
{
new SchemaExport(_configuration).Execute(true, true, false);
}
The exception seems to indicate that the Id paramter isn't being passed to the DB2 query. If I uncomment the three lines before the Get(), it works fine since the row is already cached and the Get() doesn't actually go to the database.
Here is the definition of Product.cs:
using System;
namespace Examples.DB2.Domain
{
class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Category { get; set; }
public virtual bool Discontinued { get; set; }
}
}
Here is Product.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Examples.DB2"
namespace="Examples.DB2.Domain">
<class name="Product">
<id name="Id">
<generator class="native" />
</id>
<property name="Name" />
<property name="Category" />
<property name="Discontinued" type="YesNo"/>
</class>
</hibernate-mapping>
Here is hibernate.cfg.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.DB2Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.DB2Driver</property>
<property name="connection.connection_string">Database=SAMPLE; UID=DEV; PWD=password</property>
<property name="show_sql">true</property>
</session-factory>
</hibernate-configuration>
I'm working in Visual Studio 2010 Premium on Windows 7. I'm using DB2 Express-C 9.7.4.
Ok,
I found the answer ... not exactly sure of the solution yet, but in the final release of NHibernate they added a call to AdoNet\AbstractBatcher.cs called RemoveUnusedCommandParameters. This procedure calls Driver.RemoveUnusedCommandParameters.
My solution for now is just to comment out this call and let the function do nothing.
I will bring this up with the nhusers group and see if there is a better long term solution.
thanks
dbl
I have a class LINE which contains two properties of type POINT.
I would like POINT to be a component property.
If LINE were to contain only 1 POINT, this would be no problem, but since it contains 2 POINTs I would think I needed to distinguish them (so a prefix or suffix can be applied to column names).
I tried using the PropertyName attribute of the ComponentProperty tag, but still only one set of X and Y columns is generated inside my LINE table.
For clarity, my goal is to have a LINE table with a Point1_X, Point1_Y, Point2_X and a Point2_Y column.
I use Nhibernate.Mapping.Attributes, below you can see my mapping
[Class]
public class Line : EntityBase
{
[ComponentProperty(PropertyName = "Point1")]
public UiPoint Point1 { get; set; }
[ComponentProperty(PropertyName = "Point2")]
public UiPoint Point2 { get; set; }
//omitted the constructor
}
[Component]
public class UiPoint
{
[Property]
public double X { get; set; }
[Property]
public double Y { get; set; }
//omitted the constructor
}
In the meanwhile I figured out the following XML mapping wil solve my problem
<class name="Domain.WashProcessLine,Domain">
<id name="Id" />
<component name="Point1">
<property name="X" type="Double" column="Point1_X" />
<property name="Y" type="Double" column="Point1_Y" />
</component>
<component name="Point2">
<property name="X" type="Double" column="Point2_X" />
<property name="Y" type="Double" column="Point2_Y" />
</component>
</class>
found an option on https://www.hibernate.org/hib_docs/nhibernate/html/components.html
the following tagging creates the desired table structure, but does give me a casting exception (from UiPoint to IDictionary) when retreiving the property from the database.
So I'm not entirely there yet :(
[Class]
public class Line : EntityBase
{
[DynamicComponent(1)]
[Property(2, Name = "X", Column = "Point1_X", TypeType = typeof(double))]
[Property(3, Name = "Y", Column = "Point1_Y", TypeType = typeof(double))]
public UiPoint Point1 { get; set; }
[DataMember]
[DynamicComponent(1)]
[Property(2, Name = "X", Column = "Point2_X", TypeType = typeof(double))]
[Property(3, Name = "Y", Column = "Point2_Y",TypeType=typeof(double))]
public UiPoint Point2 { get; set; }
}
After looking at the unit tests for Nhibernate.Mapping.Attributes, and trying a number of different solutions, we found that the cleanest way (unfortunately) to fix the situation provided above, was by injecting some raw xml into our mapping. This means we removed the property attributes in our line class and replaced them with a single entry as shown below
[RawXml(After=typeof(ComponentAttribute), Content = #"<component name=""Point1"">
<property name=""X"" type=""Double"" column=""Point1_X"" />
<property name=""Y"" type=""Double"" column=""Point1_Y"" />
</component>
<component name=""Point2"">
<property name=""X"" type=""Double"" column=""Point2_X"" />
<property name=""Y"" type=""Double"" column=""Point2_Y"" />
</component>")]
This question and answer helped me out while playing around with NHibernatePets Sample
I thought I'd have a go at including the above Line/Point implementation. My code is below if anyone is interested. I found some frustrating quirks with using Attributes too. The first being that if you are using multiple classes with [Id] declarations such as:
[Id(Name = "id")]
[Generator(1, Class = "native")]
then you need to specify the order number (1) for the Generator or else the generated mapping may omit the Generator attribute for one or more of your classes. Apparently this has something to do with the way VS handles things.
Another thing I found when comparing the sample Pet.hbm.xml file with the generated file output using:
//Export to a mapping file when required. Test/Production.
HbmSerializer.Default.Serialize(typeof(Pet).Assembly,"Pets.hbm.xml");
was that the Access="field" property shouldn't be set in the [Id] attribute, even though it was in the sample mapping file.
Line and UiPoint Classes (in NHibernatePets namespace)...
[Class(Lazy = true)]
public class Line
{
[Id(Name = "id")]
[Generator(1, Class = "native")]
#if useAttributes
virtual public int id { get; set; }
#else
private int id;
#endif
const string point1 =
#"<component name= ""Point1"" class= ""NHibernatePets.UiPoint"" >
<property name=""X""
type=""Double""
column=""Point1_X""/>
<property name=""Y""
type=""Double""
column=""Point1_Y""/>
</component>";
const string point2 =
#"<component name=""Point2"" class=""NHibernatePets.UiPoint"" >
<property name=""X""
type=""Double""
column=""Point2_X""/>
<property name=""Y""
type=""Double""
column=""Point2_Y""/>
</component>";
[RawXml(After = typeof(ComponentAttribute), Content = point1)]
virtual public UiPoint Point1 { get; set; }
[RawXml(After = typeof(ComponentAttribute), Content = point2)]
virtual public UiPoint Point2 { get; set; }
}
//Don't need any Attributes set on this class as it's defined in the RawXml.
public class UiPoint
{
public double X { get; set; }
public double Y { get; set; }
}
In Main()...
//Create the Line record
Line newLine = new Line
{
Point1 = new UiPoint { X = 100.1, Y = 100.2 },
Point2 = new UiPoint { X = 200.1, Y = 200.2 }
};
try
{
using (ISession session = OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(newLine);
transaction.Commit();
}
Console.WriteLine("Saved NewLine to the database");
}
}
catch (Exception e)
{ Console.WriteLine(e); }
In Public Class Program...
static ISessionFactory SessionFactory;
static ISession OpenSession()
{
if (SessionFactory == null) //not threadsafe
{ //SessionFactories are expensive, create only once
Configuration configuration = new Configuration();
#if useAttributes
{
configuration.SetDefaultAssembly("NHibernatePets");
//configuration.SetDefaultAssembly(System.Reflection.Assembly.GetExecutingAssembly().ToString());
//To use Components and other structures, AssemblyName must be set.
//configuration.SetDefaultAssembly(typeof(Pet).Assembly.ToString());
configuration.AddInputStream(NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize(typeof(Pet).Assembly));
}
#else
configuration.AddAssembly(Assembly.GetCallingAssembly());
#endif
//Export to a mapping file when required. Test/Production.
HbmSerializer.Default.Serialize(typeof(Pet).Assembly,"Pets.hbm.xml");
SessionFactory = configuration.BuildSessionFactory();
}
return SessionFactory.OpenSession();
}