I am trying to connect to a mysql database on my first Spring Project and I seem to be overlooking something really simple.
I have this bean in my application-context.xml file which is commented out!
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}" />
I know this is going to sound stupid, but in order to create a class that connects to a Mysql database using JDBC and the or the Spring JDBCTemplate, what do I do from here?
I am confused about how to populate properties in the bean above, do I do it in a superclass like this and then extend my subclasses from it.
import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class JdbcDao {
protected DriverManagerDataSource dataSource = new DriverManagerDataSource();
JdbcDao(){
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/db_name");
dataSource.setUsername("bcash");
dataSource.setPassword("");
}
}
I am confused so any help is truly apprecated,
Many Thanks
If you are using Tomcat as your application server you can do something more like this.
Define the connection with the user name/password in your server's context.xml file (as opposed to in the application):
<Resource name="jdbc/resourceNameToUse"
auth="Container"
type="javax.sql.DataSource"
username="<UserName>"
password="<Password>"
driverName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/db_name"
maxActive="100"
maxIdle="5"
validationQuery="Select 1"
useCompression="true" />
Then when you configure Spring you create a data source that looks up the resource defined above using JNDI:
<bean id="targetDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"
value="java:comp/env/jdbc/resourceNameToUse"/>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource">
<ref local="targetDataSource"/>
</property>
</bean>
At this point you can create an instance of your JdbcTemplate as a Spring bean that references your data source:
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
If you are using Spring 3 you can simply mark JdbcTemplate in your DAO as #AutoWired and Spring will match the variable name to the id of the bean you have defined and inject the JdbcTemplate for you. If you aren't using Spring 3 you can simply inject the JdbcTemplate when you define your DAO bean.
public class MyDao {
#AutoWired
private JdbcTemplate jdbcTemplate;
}
See JdbcTemplate Best Practices for further info.
Nutshell: no, your DAO wouldn't create a new datasource via new; that defeats the purpose of using Spring. Implementations should be injected, not instantiated directly; roughly:
public class TheDaoImpl implements TheDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of TheDao follow...
}
Then in your Spring config (if not using annotations), also roughly:
<bean id="theDao" class="com.bar.plugh.TheDaoImpl" p:dataSource-ref="dataSource" />
(This uses a setter; you could also use an annotation and skip the XML config.)
You could also use JdbcTemplate as your base class and save more energy.
I recommend going over the Data access with JDBC reference docs (if you're using JDBC), or the corresponding section that deals directly with your ORM choice.
Related
I am trying to convert an xml configured bean to JavaConfig.
The xml version is working, but I keep getting error when using the JavaConfig version:
Caused by: java.lang.IllegalStateException: No BeanFactory available anymore (probably due to serialization) - cannot resolve interceptor names [cacheAdvisor]
at org.springframework.aop.framework.ProxyFactoryBean.initializeAdvisorChain(ProxyFactoryBean.java:423)
Working xml configuration:
<bean id="contentLogic" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>cacheAdvisor</value>
</list>
</property>
<property name="proxyInterfaces" value="com.company.logic.ContentLogic"/>
<property name="target">
<bean class="com.company.logic.ContentLogicImpl"/>
</property>
</bean>
Not working JavaConfig:
#Configuration
public class SpringConfiguration {
#Bean
public ContentLogic getRealContentLogic() throws ClassNotFoundException {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setInterceptorNames(new String[]{"cacheAdvisor"});
factory.setTargetClass(ContentLogicImpl.class);
factory.setProxyInterfaces(new Class[]{ContentLogic.class});
return (ContentLogic) factory.getObject();
}
}
You are creating a new ProxyFactoryBean yourself without help of Spring. ProxyFactoryBean needs a BeanFactory which is injected through setBeanFactory. Actually ProxyFactoryBean implements BeanFactoryAware. That means, when Spring creates the instance, it automatically inject the FactoryBean. You would have to manage this yourself with the Java configuration. However I think the XML or annotation-based is more the standard way to config Spring. Why do you want here to convert it to Java-based config?
I need to execute SQL script before PropertyPlaceholderConfigurer is initialized in Spring's context, as soon as application properties are stored in the database and this script should insert them. But currently placeholder is initialized earlier, which leads to errors.
Is there a way to execute <jdbc:initialize-database data-source="dataSource" ... before <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" ... in Spring?
Or is there a way to initialize placeholderConfig bean later? I tried to use depends-on, lazy-init attributes for this bean, but it didn't help. Thanks in advance.
Another solution without creating any Bean:
If you've got one <jdbc:initialize-database /> you can add this property depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" to your bean <bean id="placeholderConfig" />
If you have more than one <jdbc:initialize-database /> adapt the #0.
Here is how I solved that. I created a class Initializer. This class in its constructor executes plain old sql statements (java.sql.Statement), creates table (if it doesn't exist), and inserts properties (if they are not there). The dataSource bean is passed in the context to this constructor, and placeholderConfig bean uses depends-on="initializerBean". So, properties appear in the database before they are used.
This script
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc">
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:database/drop_schema.sql" />
<jdbc:script location="classpath:database/create_schema.sql" />
<jdbc:script location="classpath:database/sample_data.sql"/>
</jdbc:initialize-database>
<!-- Other bean definitions -->
</bean>
is essentially a shortcut to
<bean class="org.springframework.jdbc.datasource.init.DataSourceInitializer" id="dataSourceInitializer">
<property name="databasePopulator" ref="resourceDatabasePopulator"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="resourceDatabasePopulator"
class="org.springframework.jdbc.datasource.init.ResourceDatabasePopulator">
<property name="scripts">
<array>
<value>classpath:database/drop_schema.sql</value>
<value>classpath:database/create_schema.sql</value>
<value>classpath:database/sample_data.sql</value>
</array>
</property>
</bean>
Note that I've added id to the DataSourceInitializer bean. Now you can reference it in PropertyPlaceholderConfigurer's depends-on attribute. That way you declare that your PropertyPlaceholderConfigurer should be created after DataSourceInitializer.
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="dataSourceInitializer"/>
In my case db was initialized after the LocalContainerEntityManagerFactoryBean had been created and Hibernate was unable to validate my schema.
The documentation explains how to deal with these problems:
http://static.springsource.org/spring/docs/3.0.0.RC3/reference/html/ch12s09.html
12.9.1.1 Initialization of Other Components that Depend on the Database
Our use case was arguably even more complex. We're using flyway for database migrations and it needs to be started before the entityManagerFactory is created. The problem for us was that <jdbc:initialize-database /> was only used in our migration tests and thus initialized in a different application context than flyway and the entityManagerFactory. So we couldn't simply use L. BIZE answer and let our flyway bean depend on org.springframework.jdbc.datasource.init.DataSourceInitializer#0 because it might not exist (i.e. in production it doesn't exist). We ended up creating a custom factory bean like this:
class OptionalBeanInitializer extends AbstractFactoryBean implements BeanFactoryAware {
private String beanName;
private BeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#Override
public Class<?> getObjectType() {
return OptionalBeanInitializer.class;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
#Override
protected Object createInstance() throws Exception {
if (beanFactory.containsBean(beanName)) {
// Initialize
beanFactory.getBean(beanName);
}
return new OptionalBeanInitializer();
}
}
Which we could use to depend on our optional dependency like this:
<bean id="optionalDataSourceInitializer" class="com.x.y.z.OptionalBeanInitializer">
<property name="beanName" value="org.springframework.jdbc.datasource.init.DataSourceInitializer#0"/>
</bean>
<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate" depends-on="optionalDataSourceInitializer">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"
depends-on="flyway, optionalDataSourceInitializer">
<property name="dataSource" ref="dataSource"/>
</bean>
The OptionalBeanInitializer will take care of initializing the org.springframework.jdbc.datasource.init.DataSourceInitializer#0 bean only if it exists.
There are two simpler ways to add the depend-on attribute only for test context, and not for production context.
1/Override the bean in the test context.
In our case here is the production context :
<bean id="placeholderConfig" />
And the unit test context :
<bean id="placeholderConfig" depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" />
Make sure that the unit test context is loaded before the production context and the good placeholderConfig bean will be instanciated, after the jdbc:initialize-database phase.
2/Another way is to use profiles
<beans profile="!test">
<bean id="placeholderConfig" .../>
</beans>
<beans profile="test">
<jdbc:initialize-database data-source="dataSource" .../>
<bean id="placeholderConfig" depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" .../>
</beans>
It is said in Spring javadoc article about DriverManagerDataSource class, that this class is very simple and that it is recommended
to use a JNDI DataSource provided by the container. Such a DataSource can be exposed as a DataSource bean in a Spring ApplicationContext via JndiObjectFactoryBean
The question is: how to accomplish this?
For example if I wish to have DataSource bean to access my custo oracle database, what I require then? What to write in context configuration etc?
To access a JNDI data source you do something like:
<bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MyDatabase"/>
</bean>
or look et the spring 'jee' schema.
The details of the database connection are configured in WebLogic, the application accesses
the database through the jndi name.
Or use Java based configuration and do it like this:
#Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException{
Context context = new InitialContext();
return (DataSource)context.lookup("jdbc.mydatasource");
}
There is another option:
<jee:jndi-lookup id="dbDataSource" jndi-name="jdbc/MyLocalDB"
expected-type="javax.sql.DataSource" />
You can use following jndi configuration.
<beans:bean id="weblogicDataSource" class="org.springframework.remoting.rmi.JndiRmiProxyFactoryBean">
<beans:property name="jndiName" value="ConnectionPoolJNDINameAsConfigured"></beans:property>
<beans:property name="jndiEnvironment">
<beans:props>
<beans:prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</beans:prop>
<beans:prop key="java.naming.provider.url">iiop://localhost:7001</beans:prop>
</beans:props>
</beans:property>
<beans:property name="serviceInterface" value="javax.sql.DataSource"></beans:property>
</beans:bean>
and you make the reference to your injected class file as
<beans:bean id="xxxx" class="xxxxxxxx">
<beans:property name="wlDataSource" ref="weblogicDataSource" />
</beans:bean>
and in your implemenation class, use
import javax.sql.DataSource;
make an instance as
private DataSource wlDataSource;
and corresponding setter. Now you are free to use JDBCTemplate or SimpleJDBCCall etc as per your implementation thinking.
Hope this will help.
I have, I might say, a quite a big issue.
I'm working on Java web application which use springs BasicDataSource to setup DB connection. I was testing the application locally and it works just fine... but, when application is online, connection to DB in some point just stuck. I was than investigating regarding connection pooling, and I figure it out that on each new HTTP request, where I have some of the queries executed, new pool is created. As I know, pooling is introduced to be reusable, and not created the each time when new DB access is involved. Or I'm wrong?
Here is my spring datasource config:
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="url"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
<property name="defaultAutoCommit" value="true"/>
<property name="defaultTransactionIsolation" value="1"/>
<property name="initialSize" value="0"/>
<property name="maxActive" value="20"/>
<property name="minIdle" value="0"/>
</bean>
Than I have configured:
<bean id="EventDao" class="my.managament.database.class">
<property name="dataSource" ref="dataSource"/>
</bean>
And mainPageController which handles all HTTP requests sent to application
<bean id="mainController" class="my.management.main.controller.class">
In the rest of application, I use gedDatabase() to acquire DB connection, and do select through JDBCTemplate.
Where am I getting wrong?
Thanks
You want to use dao and jdbcTemplate and dataSource through a connection pool. My guess the closest correct approach to your setup is having a dao which has JdbcTemplate field and a JdbcTemplate bean created with your dataSource bean. It would look like:
public class MyDAO {
private JdbcTemplate jdbcTemplate;
// your dao methods using jdbcTemplate here
}
where jdbcTemplate comes from a bean like:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource">
</bean>
You should never need to obtain a connection from dataSource (which is apache dbcp based connection pool in your case) directly. JdbcTemplate will get a connection itself when needed. I'm not sure what "gedDatabase" is but it sounds like you tried to get connection yourself and possibly forgot to close it. This would result in pool quickly running out of connections. After handling 20 requests, the subsequent requests would be stuck on attempt to get connection from the pool.
Also I don't understand why and how you see multiple pools. You have a single connection pool which can hold up to 20 connections. All your beans are created as singletons which is the default spring scope.
What is the life time of your EventDao ? You are injecting your DataSource into the dataSource property. I suspect that you are creating multiple EventDao beans and each time you create one you have a new DataSource. I think we would need to have further understanding of the code to be able to answer your question properly.
My two cents:
As far as I am concerned having code and wiring things through XML is a terrible anti-pattern.
I'm trying to set up a Spring JPA Hibernate simple example WAR for deployment to Glassfish.
I see some examples use a persistence.xml file, and other examples do not.
Some examples use a dataSource, and some do not. So far my understanding is that a dataSource is not needed if I have:
<persistence-unit name="educationPU"
transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.coe.jpa.StudentProfile</class>
<properties>
<property name="hibernate.connection.driver_class"
value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/COE" />
<property name="hibernate.connection.username" value="root" />
<property name="show_sql" value="true" />
<property name="dialect" value="org.hibernate.dialect.MySQLDialect" />
</properties>
</persistence-unit>
I can deploy fine, but my EntityManager is not getting injected by Spring.
My applicationContext.xml:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="educationPU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="StudentProfileDAO" class="com.coe.jpa.StudentProfileDAO">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="studentService" class="com.coe.services.StudentService">
</bean>
My class with the EntityManager:
public class StudentService {
private String saveMessage;
private String showModal;
private String modalHeader;
private StudentProfile studentProfile;
private String lastName;
private String firstName;
#PersistenceContext(unitName="educationPU")
private EntityManager em;
#Transactional
public String save()
{
System.out.println("*** em: " + this.em); //em is null
this.studentProfile= new StudentProfile();
this.saveMessage = "saved";
this.showModal = "true";
this.modalHeader= "Information Saved";
return "successs";
}
My web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
Are there any pieces I am missing to have Spring inject "em" in to StudentService?
Just to confirm though you probably did...
Did you include the
<!-- tell spring to use annotation based congfigurations -->
<context:annotation-config />
<!-- tell spring where to find the beans -->
<context:component-scan base-package="zz.yy.abcd" />
bits in your application context.xml?
Also I'm not so sure you'd be able to use a jta transaction type with this kind of setup? Wouldn't that require a data source managed connection pool? So try RESOURCE_LOCAL instead.
I'm confused. You're injecting a PU into the service layer and not the persistence layer? I don't get that.
I inject the persistence layer into the service layer. The service layer contains business logic and demarcates transaction boundaries. It can include more than one DAO in a transaction.
I don't get the magic in your save() method either. How is the data saved?
In production I configure spring like this:
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/ThePUname" />
along with the reference in web.xml
For unit testing I do this:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:persistence-xml-location="classpath*:META-INF/test-persistence.xml"
p:persistence-unit-name="RealPUName" p:jpaDialect-ref="jpaDialect"
p:jpaVendorAdapter-ref="jpaVendorAdapter" p:loadTimeWeaver-ref="weaver">
</bean>
If anyone wants to use purely Java configuration instead of xml configuration of hibernate, use this:
You can configure Hibernate without using persistence.xml at all in Spring like like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
{
Map<String, Object> properties = new Hashtable<>();
properties.put("javax.persistence.schema-generation.database.action",
"none");
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect"); //you can change this if you have a different DB
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
factory.setDataSource(this.springJpaDataSource());
factory.setPackagesToScan("package name");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
Since you are not using persistence.xml, you should create a bean that returns DataSource which you specify in the above method that sets the data source:
#Bean
public DataSource springJpaDataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost/SpringJpa");
dataSource.setUsername("tomcatUser");
dataSource.setPassword("password1234");
return dataSource;
}
Then you use #EnableTransactionManagement annotation over this configuration file. Now when you put that annotation, you have to create one last bean:
#Bean
public PlatformTransactionManager jpaTransactionManager()
{
return new JpaTransactionManager(
this.entityManagerFactoryBean().getObject());
}
Now, don't forget to use #Transactional Annotation over those method that deal with DB.
Lastly, don't forget to inject EntityManager in your repository (This repository class should have #Repository annotation over it).
I have a test application set up using JPA/Hibernate & Spring, and my configuration mirrors yours with the exception that I create a datasource and inject it into the EntityManagerFactory, and moved the datasource specific properties out of the persistenceUnit and into the datasource. With these two small changes, my EM gets injected properly.
This may be old, but if anyone has the same problem try changing unitname to just name in the PersistenceContext annotation:
From
#PersistenceContext(unitName="educationPU")
to
#PersistenceContext(name="educationPU")