Enabling connection pooling with HSQLDB and Spring embedded database - sql

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();

Related

Spring AMQP application configuration from XML to Java

I'm struggling with rewriting RabbitMQ application configuration from XML to Java. Sadly once the code is executed, quite general error appears:
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException:
Failed to invoke target method 'receiveMessage' with
argument type = [class [B], value = [{[B#3bd0e47}]
...
Caused by: java.lang.NoSuchMethodException: com.mycompany.MessageListener.receiveMessage([B)
Application works if I base my configuration on XML, listed below.
I tried to rewrite it, basing on Spring Integration, AMQP, Rabbit documentation. Nevertheless, spring configuration documentation is mostly xml based, hence my question.
XML conf:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<rabbit:connection-factory id="connectionFactory" host="mycompany-host"
username="mycompany-username"
password="mycompany-password"
virtual-host="mycompany-vhost"/>
<rabbit:template id="mycompany-template" connection-factory="connectionFactory" />
<rabbit:admin id="admin" connection-factory="connectionFactory" />
<!-- ##### -->
<rabbit:queue id="queue-id" name="queue-name" declared-by="admin"/>
<rabbit:direct-exchange name="mycompany-incoming-events" declared-by="admin">
<rabbit:bindings>
<rabbit:binding queue="queue-name" key="" />
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- ##### -->
<int-amqp:inbound-channel-adapter channel="mycompany-channel"
queue-names="queue-name" connection-factory="connectionFactory" />
<int:chain input-channel="mycompany-channel">
<int:transformer>
<bean class="com.mycompany.MyCompanyParser"/>
</int:transformer>
<int:filter expression="payload.header != null"/>
<int:transformer>
<bean class="com.mycompany.MyCompanyHeaderEnricher"/>
</int:transformer>
<int:recipient-list-router>
<int:recipient channel="dataSubmittedChannel"/>
</int:recipient-list-router>
</int:chain>
<int:chain input-channel="dataSubmittedChannel">
<int:filter expression="headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')" />
<int:service-activator>
<bean class="com.mycompany.MessageListener"/>
</int:service-activator>
</int:chain>
</beans>
Java listener:
#Component
public class MessageListener {
public void receiveMessage(final MyCompanyParsedType msg){
System.out.println(msg.toString());
}
}
After some rewriting I managed to came up with this Java based configuration:
import com.nxcs3.gamefetcher.configuration.SampleConfiguration;
import com.nxcs3.gamefetcher.listener.GameMessageListener;
import nxcs.drept.nxcs2events.EventHeadersEnricher;
import nxcs.drept.nxcs2events.EventParser;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter;
import org.springframework.integration.dsl.IntegrationFlow;
#SpringBootApplication
public class MyCompanySpringBootApp {
public static final String MESSAGE_QUEUE = "queue-name";
public static final String MESSAGE_EXCHANGE = "mycompany-incoming-events";
public static void main(String[] args) {
SpringApplication.run(MyCompanySpringBootApp.class);
}
#Bean
public DirectExchange exchange(){
return new DirectExchange(MESSAGE_EXCHANGE);
}
#Bean
public Queue queue(){
return new Queue(MESSAGE_QUEUE, true);
}
#Bean
public Binding binding(Queue queue){
return BindingBuilder.bind(queue).to(exchange()).with(MESSAGE_QUEUE);
}
#Bean
MessageListenerAdapter listenerAdapter(MessageListener receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
#Bean
public IntegrationFlow flow(){
return f -> f.log()
.transform(new MyCompanyParser())
.filter("payload.header != null")
.transform(new MyCompanyHeaderEnricher())
.filter("headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(MESSAGE_QUEUE);
container.setMessageListener(listenerAdapter);
return container;
}
}
I supply connection details through yaml.
As I mentioned previously, I clearly miss something.
Any ideas where did the configuration went wrong?
Added section after comments, proposed solution:
So I removed MessageListenerAdapter and replaced it using AmqpInboundChannelAdapter and #ServiceActivator
Result would look like:
#SpringBootApplication
public class MyCompanySpringBootApp {
public static final String MESSAGE_QUEUE = "queue-name";
public static final String MESSAGE_EXCHANGE = "mycompany-incoming-events";
public static void main(String[] args) {
SpringApplication.run(MyCompanySpringBootApp.class);
}
#Bean
public DirectExchange exchange(){
return new DirectExchange(MESSAGE_EXCHANGE);
}
#Bean
public Queue queue(){
return new Queue(MESSAGE_QUEUE, true);
}
#Bean
public Binding binding(Queue queue){
return BindingBuilder.bind(queue).to(exchange()).with(MESSAGE_QUEUE);
}
#Bean
public AmqpInboundChannelAdapter
channelAdapter(SimpleMessageListenerContainer container){
AmqpInboundChannelAdapter amqpInboundChannelAdapter = new
AmqpInboundChannelAdapter(container);
amqpInboundChannelAdapter.setOutputChannelName("adapter");
return amqpInboundChannelAdapter;
}
#Bean
public MessageListener handler(){
return new MessageListener();
}
#Bean
public IntegrationFlow flow(){
return f -> f.log()
.transform(new MyCompanyParser())
.filter("payload.header != null")
.transform(new MyCompanyHeaderEnricher())
.filter("headers.mycompany_enriched_header.name().equals('MY_COMPANY_CONSTRAINT')");
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(MESSAGE_QUEUE);
container.setMessageListener(listenerAdapter);
return container;
}
}
and listener:
#Component
public class MessageListener {
#ServiceActivator(inputChannel = "adapter")
public void receiveMessage(final MyCompanyParsedType msg){
System.out.println(msg.toString());
}
}
Which brings us a little bit closer, because messages are being accepted and processed inside of receiveMessage method.
However somehow coming messages do not pass through IntegrationFlow filters. Messages appear to be totally random. I added imports
The MessageListenerAdapter uses a SimpleMessageConverter by default.
And its logic is based on the presence of the contentType property.
According to your error, that sounds like there is no this property in the consumed message, therefore it falls back to the message.getBody(), which is byte[] anyway.
You may consider to specify a desired MessageConverter into that MessageListenerAdapter, e.g. SerializerMessageConverter with the ignoreContentType = true.

NancyFX + .NET Core + NHibernate

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.

Mapping in configuration file when using Nhibernate's mapping by code

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;
}

NHibernate - Sqlite in memory DB - Can't test the mapping files

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.

How to keep OpenSessionInView only use stateless session by default on Spring.NET/NHibernate?

our project only query data over a legacy database, can we use a stateless session by default when spring framework auto injected session by OSIV situation?
I mean the base class NHibernateRepository's method GetCurrentSession can retrieve a stateless session object.
Does it work that I change _sessionFactory.GetCurrentSession() to _sessionFactory.OpenStatelessSession() ?
public abstract class NHibernateRepository
{
private ISessionFactory _sessionFactory;
public ISessionFactory SessionFactory
{
protected get { return _sessionFactory; }
set { _sessionFactory = value; }
}
protected ISession CurrentSession
{
get { return _sessionFactory.GetCurrentSession(); }
}
......
But my Spring configurations will left as the regular setting:
<!-- Session Factory Configuration -->
<object id="SessionFactory" type="MyProject.Infrastructure.NHibernate.NHibernateLocalSessionFactoryObject, MyProject.Infrastructure">
<property name="DbProvider" ref="MyDbProvider"/>
<property name="MappingAssemblies">
<list>
<value>MyProject.DataAccess.NHibernateMappingLocal</value>
</list>
</property>
Anything should I do for my case? thanks for your help.