Tomcat connection pool with MyBatis - java

I was wondering if anyone has any code examples for setting up a connection pool in Tomcat (7 or later) using MyBatis as the ORM.
I presume I need to add a resource to my context.xml file in my Tomcat conf folder and then link that to MyBatis. I've had a look and any tutorials I have found seem to be Spring specific. Does anyone have a simple tutorial or can they outline the steps required to get this up and running?

There is this old entry in the iBatis FAQ that should still be applicable to myBatis: How do I use a JNDI DataSource with iBATIS in Tomcat?.
You can google for the details to configure a datasource in your Tomcat version, then configure MyBatis as well (it's different than the iBatis configuration):
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/yourDatasourceName"/>
</dataSource>
</environment>

There are a number of ways you can do this. I am assuming that you mean to implement Tomcat (as opposed to Spring) managed connection pooling. I have tested on MySQl/Spring 4/Mybatis-spring 1.1 stack.
Figure out the connection pooling mechanism you want to implement (C3P0/DBCP etc).
If you want to use your database as a JNDI data source, then you have to declare it as a resource in Tomcat settings. There are a number of ways to do this. You can follow this well written guide Tomcat JNDI connection pooling. I generally add the following entry in context.xml located in the META-INF directory of my app:
<Context>
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource" maxTotal="100" maxIdle="30" maxWaitMillis="10000" username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/javatest"/>
</Context>
This file is used by Tomcat to invoke its own default connection pool, which in this case would be DBCP 2. You can tweak its settings by adding more parameters (like removeAbandonedOnBorrow=true) in the Resource element above. You should put the driver jar (eg MySQL Connector Jar) of your database in Tomcat's lib folder.
After this, depending on your Mybatis implementation (XML or bean based), you can inject this data source into Mybatis. If you are doing it the bean way, you can do it like this:
<bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/TestDB"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbDataSource" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dbDataSource" />
<property name="typeAliasesPackage" value="com.example.model"/>
<property name="mapperLocations" value="classpath*:*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.dao" />
</bean>
MapperScannerConfigurer is necessary because it searches the directories given to it to find mapper xml files. You can also use the old way of defining a mybatis-config.xml file by injecting the file's location in the sqlSessionFactory bean in its configLocation property.

Related

Spring Data Context - lookup placeholder variables from Java map, not properties file

Currently in my data context XML file, it's looking up values to substitute from a application.properties file:
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="default">
<property name="location" value="classpath:application.properties" />
</bean>
<bean id="appleDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="${apple.url}" />
</bean>
I'd like to change this from being looked up from the application.properties file, to being read out of a Properties/Map object.
This is because the configuration could come from more than one place (i.e. not just the classpath), and this logic is already implemented in my application.
I've seen MapPropertySource but I think while that can be done when the bean is configured in Java, I'm not sure this logic can be implemented when working with the XML file alone?

Tomcat context.xml Parameter declaration accessed in Spring's application context

One of our apps declare some parameters in Tomcat's context.xml as such:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Parameter name="defaultSchema" override="true" value="demo"/>
<Resource
auth="Container"
description="DB Connection for Local Jazzee"
driverClassName="oracle.jdbc.OracleDriver"
. . .
/>
</Context>
And then in Spring's applicationContext.xml we are able to use that parameter when we instantiate some beans:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.default_schema">${defaultSchema}</prop>
. . .
</bean>
And that works fine.
Problem I'm having is that I'm trying to duplicate this in another application, and it's not working. When I try to start the app, I get the error:
Invalid bean definition with name 'sessionFactory' defined in class path resource [db/applicationContext.xml]: Could not resolve placeholder 'defaultSchema'
It's the same developer Tomcat instance in both cases, so the Tomcat version is the same. The Spring library versions appear to be the same as well. Also, if I hardcode the schema name in the application context, it works fine. So the dataSource is fine.
Any thoughts on what might be breaking this functionality?

How do I add JDBC drivers and configure JDBC Resources in Payara Micro?

What are my options to set up JDBC drivers and resources when using Java EE Payara Micro?
This method combines answers from Mike and Adam Bien via tainos. It involves making a new domain.xml, which is a Payara config file. No application modification is needed if it worked with full Payara. The example below is for PostgreSQL JDBC.
Open payara-micro.jar with an archive manager and extract the file /microdomain.xml.
Open microdomain.xml in a text editor.
If your application was already deployed to a full Payara, you can copy-paste the changes below from the full Payara's domain.xml.
Add right above the line containing </resources>, using your dbname, dbuser, dbpassword, hostname:port and poolname:
<jdbc-connection-pool connection-validation-method="auto-commit" driver-classname="org.postgresql.Driver" res-type="java.sql.Driver" name="poolname" is-connection-validation-required="true" connection-creation-retry-attempts="3" validate-atmost-once-period-in-seconds="60">
<property name="URL" value="jdbc:postgresql://localhost:5432/dbname"></property>
<property name="user" value="dbuser"></property>
<property name="password" value="dbpassword"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="poolname" jndi-name="jdbc/poolname"></jdbc-resource>
Add right above the line containing </server>:
<resource-ref ref="jdbc/poolname"></resource-ref>
Save and close the text editor.
Start Payara micro from command line, using your paths and filenames. Linux syntax:
java -cp "/opt/jdbc/postgresql.jar:/opt/payara/micro.jar" fish.payara.micro.PayaraMicro --deploy webapp.war --domainConfig microdomain.xml
Add the datasource definition to your web.xml and then add the jar file for the JDBC jar into your WEB-INF/lib. Then deploy the war file as usual to Payara Micro.
<data-source>
<name>java:global/ExampleDataSource</name>
<class-name>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</class-name>
<server-name>localhost</server-name>
<port-number>3306</port-number>
<database-name>mysql</database-name>
<user>root</user>
<password>root</password>
<!-- Example of how to use a Payara specific custom connection pool setting -->
<property>
<name>fish.payara.sql-trace-listeners</name>
<value>com.sun.gjc.util.SQLTraceLogger</value>
</property>
</data-source>
There is a complete example of how to do this on the Payara Examples GitHub repository. See Datasource example on Payara GitHub
You can configure JDBC in a normal domain.xml and supply that to Payara. If you're unsure, you can always take an existing domain.xml and use the JDBC configuration from that.
Payara Micro has a few command line options, one of which allows you to specify an alternative domain.xml file:
java -jar payara-micro.jar --deploy myApp.war --domainConfig mydomain.xml
If you're bootstrapping Payara Micro programmatically, you would use:
setAlternateDomainXML(File alternateDomainXML)
As the accepted answer didn't work for me a figured a different and slightly easier way. You still rely on the custom domain.xml, but the startup command can be simplified:
java -jar /opt/payara/payara-micro.jar --deploy webapp.war --domainConfig domain.xml --addJars /opt/mysql-connector-java-5.1.40-bin.jar
This call doesn't require you to know the Main class.
Adam Bien answered this question in his 19th Airhacks video.
My take, When used with custom resources is best used as an embedded server, in the main we configure the JDBC resources and with maven dependencies we include the needed drivers inside the jar or war files.
One of options is glassfish-resources.xml
<!-- db1 -->
<jdbc-connection-pool
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" name="db1"
res-type="javax.sql.DataSource"
steady-pool-size="1"
is-connection-validation-required="true"
connection-validation-method="meta-data"
max-pool-size="10">
<property name="password" value="icoder_pwd"/>
<property name="user" value="icoder_user"/>
<property name="databaseName" value="icoder_db"/>
<property name="serverName" value="localhost"/>
<property name="portNumber" value="3310"/>
<property name="zeroDateTimeBehavior" value="convertToNull"/>
</jdbc-connection-pool>
<jdbc-resource pool-name="db1" jndi-name="jdbc/db1"/>
<!-- db2 -->
<jdbc-connection-pool
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" name="db2"
res-type="javax.sql.DataSource"
steady-pool-size="1"
is-connection-validation-required="true"
connection-validation-method="meta-data"
max-pool-size="10">
<property name="password" value="icoder_pwd"/>
<property name="user" value="icoder_user"/>
<property name="databaseName" value="icoder_db"/>
<property name="serverName" value="localhost"/>
<property name="portNumber" value="3311"/>
<property name="zeroDateTimeBehavior" value="convertToNull"/>
</jdbc-connection-pool>
<jdbc-resource pool-name="db2" jndi-name="jdbc/db2"/>
</resources>
Complete example with entity manager implementation you can find:
https://github.com/igorzg/payara-micro-jpa-multi-tenancy

log4j and spring

I am developing a Java desktop application and I am using Spring with it. Now I want to inject log4j to my classes using applicationContext.xml. My log4j properties file is placed in a source folder Resources/log4j.properties
During my search I found out that there are many way to it when its a web application but I found out no help regarding a desktop application.
I am using Apache commons interfaces in my source code and now I want to inject log4j dependency.
Kindly, help me out..
By default, Log4J will simply read its configuration from a "log4j.properties" file in the root of the class path. Since you placed your log4.properties file in the resources source folder, this should work.
If you do not want to store your log4j configuration in your class path then you can use something like this :
<bean id="log4jInitialization"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass"
value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>myPropertiesFolder/log4j.xml</value>
</list>
</property>
</bean>

Spring Jndi Configuration, Server.xml

I have a problem setting up my configuration for JNDI with Spring. I checked the other posts but could not get my problem solved. I am using Tomcat 6 as my container. From my understanding I need to set up a resource on the server. So in my server.xml file I have this:
<GlobalNamingResources>
<Resource auth="Container" driverClassName="org.postgresql.Driver"
maxActive="100" maxIdle="5" maxWait="10000"
minEvictableIdleTimeMillis="60000" name="jdbc/myTomcatPool"
password="password" testOnBorrow="true" testWhileIdle="true"
timeBetweenEvictionRunsMillis="10000" type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/postgis" username="postgres"
validationQuery="SELECT 1"/>
</GlobalNamingResources>
I have the following in my spring-context.xml (which is on the classpath):
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool" />
<bean id="geoCodeService" class="com.sample.SampleImpl">
<property name="dataSource" ref="geoCodeData"/>
</bean>
I then have this in file META-INF/context.xml:
<Context path="/myApp" reloadable="true" cacheMaxSize="51200"
cacheObjectMaxSize="2560">
<ResourceLink global="jdbc/myTomcatPool" name="jdbc/myTomcatPool"
type="javax.sql.DataSource"/>
</Context>
My server starts up free of errors.
When I try to run the following test (that worked before I added the JNDI code):
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring-context.xml");
}
}
I get the following error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'geoCodeData': Invocation of init method failed;
nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Is my configuration wrong or is the way I am trying to run the test incorrect?
When you run your testcases, you will want to use JDBC instead of JNDI lookup. The simple reason is because you usually don't run your testcases from the application server. Thus, JNDI lookup will fail.
What I do on my end is to place data source in a separate file. I have one file for production that uses JNDI:-
project-datasource.xml
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool"></jee:jndi-lookup>
... another another file for unit test that uses JDBC:-
project-datasource-test.xml
// use the same bean name "geoCodeData"
<bean id="geoCodeData" class="...">
<property name="driverClassName" value="..." />
<property name="url" value="..." />
<property name="username" value="..." />
<property name="password" value="..." />
</bean>
The web app will use project-datasource.xml whereas the unit test will use project-datasource-test.xml.
After several hours of tearing out my (already sparse) hair I have managed to get my tomcat server to pool database connections to Oracle and use the Spring framework.
I though I would reply to this question even though there seems to be an answer, just in case it helped anybody else.
What I wanted:
A connection pool administered by Tomcat (rather than per servlet) and the config for the DB connection in the Tomcat server config (again, rather than the servlet config files).
We have several instances of Tomcat and each one connects to a specific Oracle DB, but developers are producing servlets that could be required to run on any one of them, hence I don't want the DB connection details in the WAR file they produce, but to let them look it up and be provided with that server's data source, via JNDI.
In the Tomcat server conf/context.xml I added the following code:
<Resource name="jdbc/banner"
auth="Container"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#DBan8DB1.example.ac.uk:1522:bde8"
username="dbsro"
password="verysecret"
initialSize="5"
maxActive="20"
maxIdle="10"
removeAbandoned="true"
global="jdbc/banner"
maxWait="-1"/>
Obviously I have the ojdbc, pool and dbcp JAR files present in Tomcat's lib directory on the server.
An important point to note here is that the type is of "javax.sql.DataSource" rather than "org.apache.commons.dbcp.BasicDataSource" which I originally thought it should be.
Now in the Web Application's WEB-INF/web.xml I added the following:
<resource-ref>
<description>Oracle Banner Datasource</description>
<res-ref-name>jdbc/banner</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and in my Web Application's servlet-context.xml file (where ever you keep yours might vary) I had this. This is not the whole file but the XML name space is important here, for the jee parts:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/banner" resource-ref="true" />
Again note the fully qualified reference jndi-name="java:comp/env/jdbc/banner" which seemed to be required. Why is isn't needed in the resource-ref section of the web.xml file, I have no idea.
If anyone has any thoughts on this I would be pleased to read them.
By the way, this URL helped: Tomcat6 JNDI data source how-to
After all that, the connection worked. So I mopped the blood, sweat and tears from my work station and enjoyed a delicious cup of fresh coffee.

Categories

Resources