Spring JPA, Hibernate, Data XML configuration

Here’s a cleaned up, plug’n’play version of a Spring database XML config taken from a demo project employing Spring Data for the Repositories interfaces and Hibernate behind JPA.

<beans 	xmlns="http://www.springframework.org/schema/beans"
		xmlns:p="http://www.springframework.org/schema/p"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:tx="http://www.springframework.org/schema/tx"
		xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
		  p:entityManagerFactory-ref="entityManagerFactory" />


	<jpa:repositories base-package="com.to.my.data.repository"
					  transaction-manager-ref="transactionManager"
					  entity-manager-factory-ref="entityManagerFactory"/>

	<tx:annotation-driven transaction-manager="transactionManager"/>
	

	
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
					p:database="${database.database}"
					p:databasePlatform="${database.databasePlatform}"
					p:showSql="${database.showSql}"
					p:generateDdl="${database.generateDdl}" />
		</property>
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.dialect">${database.databasePlatform}</prop>
				<prop key="hibernate.max_fetch_depth">3</prop>
				<prop key="hibernate.fetch_size">50</prop>
				<prop key="hibernate.batch_size">10</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
		<property name="packagesToScan" value="com.to.my.bean"/>
	</bean>
	
	<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"
			p:driverClassName="${database.driverClassName}"
			p:url="${database.url}"
			p:username="${database.username}"
			p:password="${database.password}"
			p:testOnBorrow="true"
			p:testOnReturn="true"
			p:testWhileIdle="true"
			p:timeBetweenEvictionRunsMillis="1800000"
			p:numTestsPerEvictionRun="3"
			p:minEvictableIdleTimeMillis="1800000"
			p:initialSize="1"
			p:maxActive="50"
			p:maxIdle="20"/>

</beans>
Advertisements

Spring Transaction Management Best Practices

In this post I’ll create a placeholder that I’ll keep updating in the future accumulating Spring Transaction Management best practises that I find along the way:

  • Keep the @Transactional definition at the Service layer and not the DAO layer. Service beans might use multiple DAOs ACID-ly under the same transaction. If otherwise transaction management is defined at the DAO level, Service beans will pay the price of creating multiple transactions for a conceptually grouped operation let alone the data inconsistencies we’ll risk having not defining ACID Service operations.
  • Further underlying the previous point we can define @Transactional(propagation = Propagation.MANDATORY) at the class level of our DAOs, therefore enforcing the consumers of our DAOs to initiate transaction management.
  • Know what are the defaults of the @Transactional annotation and don’t just use it by faith. Namely, if not specified otherwise, propagation is set to Propagation.REQUIRED which means use an existing transaction otherwise create a new one; isolation is set to Isolation.DEFAULT that is defined by the underlying DB default which normally results to Isolation.READ_COMMITED; readOnly flag is switched off by default; rollbackFor can be defined for a Throwable class but beware: by default rollback occurs only if a RuntimeException is thrown unless this parameter is setup.
  • Be careful of the readOnly flag. Although @Transactional(readOnly = true, propagation=Propagation.REQUIRED) will throw an exception upon a JDBC insert/update command within that transaction, it wouldn’t have the expected behavior on an insert/update ORM operation where the operation will unintuitively go through and be committed successfully. Under an ORM environment use the flag along with Propagation.SUPPORTS. In that case we won’t have to pay for the cost of creating a new transaction simply for a select operation unless there is one in place. Or even still consider ditching the whole @Transactional management for select operations.
  • Be careful when using Propagation.REQUIRES_NEW that is not in the top level. It causes problems more times than not. Since every time a new transaction is wrapping that aspect, in cases where multiple REQUIRES_NEWs are included within the same transactional service method in cases of a rollback ACID is not respected and inconsistent data are left in the DB. On the other hand, just using it on the top level of the transaction method is fine and actually equates the default Propagation.REQUIRED

Spring Email Testing using GreenMail

I’ve been ignoring setting up a proper unit-test regarding email functionality for some time now mainly because most of the cases a quick and dirty test email to myself would do it. Well, quick’n’dirty hack no more and this is a proper way to test your Spring Email functionality using an easy to use library called GreenMail.

Let’s start with the POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>SpringEmailTest</groupId>
    <artifactId>SpringEmailTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!--Spring Dependencies-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--Testing Dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>com.icegreen</groupId>
            <artifactId>greenmail</artifactId>
            <version>${greenmail.version}</version>
        </dependency>
        <!--Email Dependencies -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>${javax-mail.version}</version>
        </dependency>


    </dependencies>
    <properties>
        <java.version>1.6</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>3.2.2.RELEASE</spring.version>
        <junit.version>4.10</junit.version>
        <javax-mail.version>1.5.0</javax-mail.version>
       <greenmail.version>1.3.1b</greenmail.version>
    </properties>


</project>

The Spring Configuration looks like this:

package com.dimitrisli.springEmailTest.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.util.Properties;

@Configuration
@ImportResource(value = "classpath:/spring/appXMLContext.xml")
@PropertySource(value = "classpath:/properties/application.properties")
public class AppConfig {

    @Bean
    public JavaMailSenderImpl emailSender(@Value("${email.host}") String emailHost,
                                          @Value("${email.port}") Integer emailPort,
                                          @Value("${email.username}") String username,
                                          @Value("${email.pass}") String password){
        JavaMailSenderImpl emailSender = new JavaMailSenderImpl();
            emailSender.setHost(emailHost);
            emailSender.setPort(emailPort);
            emailSender.setUsername(username);
            emailSender.setPassword(password);
            //emailSender.setDefaultEncoding("UTF_8");
            Properties mailProps = new Properties();
                mailProps.setProperty("mail.transport.protocol","smtp");
                mailProps.setProperty("mail.smtp.auth","true");
                mailProps.setProperty("mail.smtp.starttls.enable","true");
                mailProps.setProperty("mail.debug","false");
                emailSender.setJavaMailProperties(mailProps);
        return emailSender;
    }
}

A few things to note here:
– The @Configuration annotation marks the Spring Java Configuration context.
– The @PropertySource is retrieving the properties file from classpath. Apparently @PropertySource is not playing well with @Value injection therefore a workaround this is to include an XML context just to incude the property placeholder reference.
– The @ImportResource is the way of including an XML context file from the classpath.
– The beans are defined using the @Bean annotation.
– The @Value annotation is automatically injecting the properties values into the variables.

The referenced application.properties that is including the Email details (in this case setup to be Gmail) looks like this:

email.host=smtp.gmail.com
email.port=25
email.username=yourGmailAtgmailDotcom
email.pass=yourPass

As noted above to resolve an inconsistency between the @PropertySource and @Value we need to introduce an XML context that looks like this:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--In Java @Configuration POJOs the @PropertySource is
        not playing well with @Value. To resolve this we introduce
        just this reference that is doing the trick. No other dependency on
        XML configuration should be needed.-->
    <context:property-placeholder />
</beans>

Finally the JUnit test looks like this:

package com.dimitrisli.springEmailTest;

import com.dimitrisli.springEmailTest.config.AppConfig;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.GreenMailUtil;
import com.icegreen.greenmail.util.ServerSetupTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import javax.mail.Message;
import javax.mail.MessagingException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class EmailTest {

    @Resource
    private JavaMailSenderImpl emailSender;

    private GreenMail testSmtp;

    @Before
    public void testSmtpInit(){
        testSmtp = new GreenMail(ServerSetupTest.SMTP);
        testSmtp.start();

        //don't forget to set the test port!
        emailSender.setPort(3025);
        emailSender.setHost("localhost");
    }

    @Test
    public void testEmail() throws InterruptedException, MessagingException {
        SimpleMailMessage message = new SimpleMailMessage();

        message.setFrom("test@sender.com");
        message.setTo("test@receiver.com");
        message.setSubject("test subject");
        message.setText("test message");
        emailSender.send(message);
        
        Message[] messages = testSmtp.getReceivedMessages();
        assertEquals(1, messages.length);
        assertEquals("test subject", messages[0].getSubject());
        String body = GreenMailUtil.getBody(messages[0]).replaceAll("=\r?\n", "");
        assertEquals("test message", body);
    }

    @After
    public void cleanup(){
        testSmtp.stop();
    }
}

– JUnit is configured to run with Spring using the SpringJUnit4ClassRunner.class
– JUnit is loading the Spring context as instructed by the @ContextConfiguration annotation pointing to the Java Configuration class.
– The @Resource annotation is autowiring the bean dependency directly on our test conveniently having it initialised during the test runtime.
– We have an init() activity marked by JUnit’s @Before annotation where we instantiate our GreenMail mail server and changing the port of our email service to a test one.
– The test method itself is a simple manner of initialising a test SimpleMailMessage and sending it via our email service that gets intercepted by GreenMail and further queried upon concerning the data received.
– A cleanup functionality is closing the resourced during the @After JUnit instructed method.

The source code can be found in this Github repo.

Spring, JdbcTemplate, Oracle example

This is a demonstration of how Spring and Oracle are playing happily together via JdbcTemplate.

Prerequisites:

Let’s start from the end: this is how the JUnit 4 test case looks like for the DAO of our domain object for all its CRUD operations armored with rollback functionality so our database data are not being altered after our transactional operations. Notice the use of matchers and the spring-context-enabling annotation of the test class:

package com.dimitrisli.springJdbcOracle.dao.impl;

import com.dimitrisli.springJdbcOracle.dao.LocationDao;
import com.dimitrisli.springJdbcOracle.model.Location;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/spring/context/applicationContext.xml")
@TransactionConfiguration(transactionManager = "jdbcTransactionManager", defaultRollback = true)
@Transactional
public class LocationDaoTest {

    @Inject private LocationDao locationDao;

    @Test
    public void testSelectAllLocations(){
    List<Location> locations = locationDao.getLocations();
    assertThat(locations.size(), is(23));
    }

    @Test
    public void testSelectOneLocation(){
      Location location = locationDao.getLocation(1000L);
      assertNotNull("test entry not found", location);
    }

    @Test
    public void testDeleteLocation(){
        assertNotNull("entry for test should be there", locationDao.getLocation(1000L));
        locationDao.deleteLocation(1000L);
        assertNull("entry wasn't successfully deleted", locationDao.getLocation(1000L));
    }

    @Test
    public void testInsertLocation(){
        Location location = new Location(1000L,"test","11111","athens","athens","IT");
        int sizeBeforeInsert = locationDao.getLocations().size();
        locationDao.createLocation(location);
        assertThat(locationDao.getLocations().size(),is(sizeBeforeInsert + 1));
    }

    @Test
    public void testUpdateLocation(){
        Location newLocation = new Location(1000L,"test","11111","athens","athens","IT");
        locationDao.updateLocation(newLocation);
        Location changedLocation = locationDao.getLocation(1000L);
        assertThat(changedLocation.getStreetAddress(), is("test"));
    }

}

The POM looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>SpringJdbcOracle</groupId>
    <artifactId>SpringJdbcOracle</artifactId>
    <version>1.0</version>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>

            <!--Logging related plugin
                this plugin breaks the build if non-wanted logging frameworks are spotted in the classpath
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>1.0.1</version>
                <executions>
                    <execution>
                        <id>enforce-versions</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <bannedDependencies>
                                    <excludes>
                                        <exclude>commons-logging:commons-logging</exclude>
                                        <exclude>log4j:log4j</exclude>
                                    </excludes>
                                </bannedDependencies>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!--Spring related dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>


        <!--Oracle jdbc driver-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>

        <!-- DB Connection Pool -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>

        <!--Logging related dependencies
            Further info: http://www.slf4j.org/faq.html#excludingJCL and
                          http://blog.frankel.ch/configuring-maven-to-use-slf4j
        -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <!--scope should be runtime but applied at compile time
                to get autocompletion visibility at logback.xml-->
            <!--scope>runtime</scope-->
            <version>0.9.24</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.2</version>
        </dependency>

        <!-- JUnit 4 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!-- Misc -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
            <scope>runtime</scope>
        </dependency>


    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>3.1.2.RELEASE</spring.version>
     </properties>


</project>

Notes:
– Note how we explicitly piping any commons logging or log4j logging through our logback slf4j wrapper
– Notice how we are declaring our Oracle driver dependency given that it’s already installed in our local Maven repo (see prerequisites section)

The applicationContext:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="classpath*:spring/database/database.xml"/>
    <context:component-scan base-package="com.dimitrisli.springJdbcOracle" />
</beans>

The imported database context above is:

<?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:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
           p:location="properties/database.properties" />

    <bean  id="dataSource"
           class="org.apache.commons.dbcp.BasicDataSource"
           destroy-method="close"
           p:driverClassName="${jdbc.driverClassName}"
           p:url="${jdbc.url}"
           p:username="${jdbc.username}"
           p:password="${jdbc.password}" />

    <bean  class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"
           c:dataSource-ref="dataSource"  />

    <bean  id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
            p:dataSource-ref="dataSource"/>
    <tx:annotation-driven transaction-manager="jdbcTransactionManager"/>

</beans>

Notes:

– We are using DBCP for our DB connection pool datasource
– We are using the parameter namespace to save on some attribute injection open-close xml characters
– We are explicitly stating NamedParameterJdbcTemplate as our jdbcTemplate and inject it with the needed datasource so we can have it available for injection conveniently in our DAOs.

The domain POJO object we are about to play with is the Location object that corresponds to the Locations table on the Oracle HR schema:


package com.dimitrisli.springJdbcOracle.model;

public class Location {

    private Long locationId;
    private String streetAddress;
    private String postalCode;
    private String city;
    private String stateProvince;
    private String countryId;

    public Location(Long locationId, String streetAddress, String postalCode, String city, String stateProvince, String countryId) {
        this.locationId = locationId;
        this.streetAddress = streetAddress;
        this.postalCode = postalCode;
        this.city = city;
        this.stateProvince = stateProvince;
        this.countryId = countryId;
    }

//getters, hashcode(), equals(), toString() ignored for brevity

The RowMapper that will provide Location objects having resultSets coming from the DB. It’s a factory method (although not static but stateless by our design) used internally from Spring per DB line result returned:

package com.dimitrisli.springJdbcOracle.orm;

import com.dimitrisli.springJdbcOracle.model.Location;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.SQLException;

@Component
public class LocationRowMapper implements RowMapper<Location> {

    @Override
    public Location mapRow(ResultSet rs, int rowNum) throws SQLException {
        return  new Location(rs.getLong("LOCATION_ID"),
                             rs.getString("STREET_ADDRESS"),
                             rs.getString("POSTAL_CODE"),
                             rs.getString("CITY"),
                             rs.getString("STATE_PROVINCE"),
                             rs.getString("COUNTRY_ID"));
    }
}

Here’s the DAO interface responsible for the CRUD operations:

package com.dimitrisli.springJdbcOracle.dao;

import com.dimitrisli.springJdbcOracle.model.Location;

import java.util.List;

public interface LocationDao {

    public void createLocation(Location location);
    public List<Location> getLocations();
    public Location getLocation(Long locationId);
    public void updateLocation(Location location);
    public void deleteLocation(Long locationId);

}

and its implementation looks like this:


package com.dimitrisli.springJdbcOracle.dao.impl;

import com.dimitrisli.springJdbcOracle.dao.LocationDao;
import com.dimitrisli.springJdbcOracle.model.Location;
import com.dimitrisli.springJdbcOracle.orm.LocationRowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;

import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;

@Repository("locationDao")
public class LocationDaoImpl implements LocationDao {

    private static final String CREATE_SQL = "INSERT INTO LOCATIONS( LOCATION_ID, STREET_ADDRESS, POSTAL_CODE, CITY, " +
                                             "STATE_PROVINCE, COUNTRY_ID) " +
                                             "VALUES (LOCATIONS_SEQ.NEXTVAL, :streetAddress, :postalCode, :city, " +
                                             ":stateProvince, :countryId)";

    private static final String GET_ALL_SQL = "SELECT LOCATION_ID, STREET_ADDRESS, POSTAL_CODE, CITY, STATE_PROVINCE, COUNTRY_ID " +
                                              "FROM LOCATIONS";

    private static final String GET_SQL = "SELECT LOCATION_ID, STREET_ADDRESS, POSTAL_CODE, CITY, STATE_PROVINCE, COUNTRY_ID " +
                                          "FROM LOCATIONS WHERE LOCATION_ID = :locationId";

    private static final String DELETE_SQL = "DELETE LOCATIONS WHERE LOCATION_ID = :locationId";

    private static final String UPDATE_SQL = "UPDATE LOCATIONS SET STREET_ADDRESS = :streetAddress, POSTAL_CODE=:postalCode, " +
                                            "CITY = :city, STATE_PROVINCE = :stateProvince, COUNTRY_ID = :countryId " +
                                            "WHERE LOCATION_ID = :locationId";

    @Inject private NamedParameterJdbcOperations jdbcTemplate;
    @Inject private LocationRowMapper locationRowMapper;

    @Override
    public void createLocation(Location location) {
        SqlParameterSource params = new MapSqlParameterSource()
                .addValue("streetAddress", location.getStreetAddress())
                .addValue("postalCode", location.getPostalCode())
                .addValue("city", location.getCity())
                .addValue("stateProvince", location.getStateProvince())
                .addValue("countryId", location.getCountryId());
        jdbcTemplate.update(CREATE_SQL, params);
    }

    @Override
    public List<Location> getLocations() {
        return jdbcTemplate.query(GET_ALL_SQL, new HashMap<String, Object>(), locationRowMapper);
    }

    @Override
    public Location getLocation(Long locationId) {
        SqlParameterSource params = new MapSqlParameterSource()
                .addValue("locationId", locationId);
        List<Location> locations = jdbcTemplate.query(GET_SQL, params, locationRowMapper);
        return locations.isEmpty()?null:locations.get(0);
    }

    @Override
    public void updateLocation(Location location) {
        SqlParameterSource params = new MapSqlParameterSource()
                .addValue("locationId", location.getLocationId())
                .addValue("streetAddress", location.getStreetAddress())
                .addValue("postalCode", location.getPostalCode())
                .addValue("city", location.getCity())
                .addValue("stateProvince", location.getStateProvince())
                .addValue("countryId", location.getCountryId());
        jdbcTemplate.update(UPDATE_SQL, params);
    }

    @Override
    public void deleteLocation(Long locationId) {
        jdbcTemplate.update(DELETE_SQL, new MapSqlParameterSource("locationId",locationId));
    }
}

Notes:
– Notice how we inject the JdbcTemplate and not fetching it from this class directly
– Notice how we inject the RowMapper and we don’t anonymous-class-it from this class directly
– The CRUD operations are setup parameterized in the top of the file as private static finals
– In all the CRUD operations we are using either jdbcTemplate.update() or jdbcTemplate.query() methods

Here’s the Github repo of the project

Maven install ojdbc6

I really wished the Oracle driver jar was part of any (legal) publicly available Maven repo, but it’s not. So we’ll have to take matters on our hands and install it in our local repo once and for all so we can effortlessly thereafter summon it via our pom file:

       <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.3</version>
        </dependency>

Steps:

  • Download the jdbc6.jar from the Oracle website. I tried to automate this step via a Groovy script but this pesky agreement radio-button gets in the way (which is there for a reason to be fair)
  • Supposing mvn is already setup in your path:
  • mvn install:install-file -Dfile=ojdbc6.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.3 -Dpackaging=jar -DgeneratePom=true
    

Scala, Spring, Maven example

This is a quick, bare minimum example of how Scala, Spring and Maven live happily together.

First, in the default directory structure that Maven offers out of the box, we need to create two new dirs:

  • /src/main/scala
  • /test/main/scala

If you are managing the project from within Eclipse or IntelliJ you’ll have to denote these dirs as source folders explicitly.

The POM looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ScalaSpringMaven</groupId>
    <artifactId>ScalaSpringMaven</artifactId>
    <version>1.0</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
            </plugin>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin></plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-compiler</artifactId>
            <version>2.9.1</version>
        </dependency>
    </dependencies>

</project>
 

Note how the Scala compiler plugin hijacks the compile and testCompile phases of Maven lifecycle and happily co-exists with the Java compiler.

Next, the application context looks as follows:

<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<context:component-scan base-package="com.dimitrisli.scala"/>

</beans>

The above Spring config file looks identical like in any other Java project where we want to activate component scanning for post Spring 3 annotation-enabled beans.
Our Bean looks like:

package com.dimitrisli.scala

import org.springframework.stereotype.Component

@Component
class MyBean {

  def performTask = "Greetings from MyBean!"
}

and the Scala Object where we startup the Spring context:

package com.dimitrisli.scala

import org.springframework.context.ApplicationContext
import org.springframework.context.support.ClassPathXmlApplicationContext


object Application {

  def main(args: Array[String]) {

    val context:ApplicationContext = new ClassPathXmlApplicationContext("application-context.xml")
    val myBean:MyBean = context.getBean("myBean").asInstanceOf[MyBean]
    print(myBean performTask)
  }
}

The code can be found in this Github repository.

How to postpone Spring Application Context instantiation

We are first calling the two args application context constructor passing false as a flag:

 ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"config/myApplicationContext.xml"}, false );

then whenever we are done with our processing we handpick and load the beans we are interested to include:

context.getBeanFactory().registerSingleton("dataSource", myTestDataSource);

finally we initialise manually:

context.refresh();

Sample Spring Database Configuration

This is a sample Spring Database context:

<?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:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
           p:location="properties/database.properties" />

    <bean  id="dataSource"
           class="org.apache.commons.dbcp.BasicDataSource"
           destroy-method="close"
           p:driverClassName="${jdbc.driverClassName}"
           p:url="${jdbc.url}"
           p:username="${jdbc.username}"
           p:password="${jdbc.password}" />

    <bean  class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"
           c:dataSource-ref="dataSource"  />

    <bean  id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
            p:dataSource-ref="dataSource"/>
    <tx:annotation-driven transaction-manager="jdbcTransactionManager"/>

</beans>

Notes:

  • PropertyPlaceholder bean is loading up our database properties file
  • We are using a DBCP BasicDataSource as our datasource where we are setting user/pass/url/driver parameters from the properties files
  • We are supposing that DBCP, Spring, DB Driver related jars are all found in the classpath most commonly by a Maven POM configuration
  • NamedParameterJdbcTemplate is set in this config file so that  it can be conveniently referred from the application to be injected by will. Note the datasource reference we are passing as a parameter to its bean
  • Spring DataSourceTransactionManager is getting used instantiating its bean with the datasource. That will allow us to mark methods @Transactional which will get intercepted by Spring AOP and read our parameter/attribute strategies and other setup as per the ACID operations.

Spring, Hibernate, MySQL integration example

This is a working example showcasing Spring, Hibernate and MySQL integration in a Maven project.

The POM looks like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dimitrisli</groupId>
  <artifactId>SpringMySQL</artifactId>
  <version>1.0</version>
  <build>
  	<finalName>SpringHibernateMySQL</finalName>
  	<plugins>
  		<plugin>
  			<groupId>org.apache.maven.plugins</groupId>
  			<artifactId>maven-compiler-plugin</artifactId>
  			<version>2.3.2</version>
  			<configuration>
  				<source>1.6</source>
  				<target>1.6</target>
  				<encoding>UTF-8</encoding>
  			</configuration>
  		</plugin>
  	</plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.18</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>3.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
	<dependency>
	  <groupId>org.hibernate</groupId>
	  <artifactId>hibernate-core</artifactId>
	  <version>3.6.9.Final</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-orm</artifactId>
		<version>3.1.0.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>javassist</groupId>
		<artifactId>javassist</artifactId>
		<version>3.12.1.GA</version>
	</dependency>
  </dependencies>
  <properties>
  	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>

Our MySQL database script:

create database javaTestDB;
use javaTestDB;
CREATE USER javauser IDENTIFIED BY 'javapass';
grant usage on *.* to javauser@localhost identified by 'javapass';
grant all privileges on javaTestDB.* to javauser@localhost;

CREATE TABLE PERSON_RECORD (
	ID INT NOT NULL AUTO_INCREMENT,
	NAME VARCHAR(15) NOT NULL,
	SURNAME VARCHAR(15) NOT NULL,
	PRIMARY KEY (ID)
);

This is basically a table that is representing our Person Java bean:

package com.dimitrisli.springHibernateMySQL.model;

import java.io.Serializable;

public class Person implements Serializable {

	private static final long serialVersionUID = 1L;
	private Integer id;
	private String name;
	private String surname;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSurname() {
		return surname;
	}
	public void setSurname(String surname) {
		this.surname = surname;
	}

	public void setId(Integer id){
		this.id = id;
	}

	public Integer getId(){
		return this.id;
	}
}

Each DB table record represents a Person Object. The Person CRUD operations are represented in this interface:

package com.dimitrisli.springHibernateMySQL.dao;

import com.dimitrisli.springHibernateMySQL.model.Person;

public interface PersonORMDao {

	public void create(Person person);
	public Person read(String name, String surname);
	public void update(Person person);
	public void delete(Person person);
}

Since we are using Spring it’s a good practise to have an associated interface for every DAO bean we are planning to have since Spring behind the scenes is creating a proxy between this interface and the POJO before creating the bean.

The implementation of this interface looks like this:

package com.dimitrisli.springHibernateMySQL.dao;

import java.util.List;

import com.dimitrisli.springHibernateMySQL.model.Person;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class PersonDaoHibernateImpl extends HibernateDaoSupport implements PersonORMDao{

	public void create(Person person) {
		getHibernateTemplate().save(person);
	}

	public Person read(String name, String surname) {
		List list = getHibernateTemplate().find("from PERSON_RECORD where NAME=? AND SURNAME=?",name,surname);
		return (Person) list.get(0);
	}

	public List readAll() {
		List list = getHibernateTemplate().find("from " + Person.class.getName());
		return list;
	}

	public void update(Person person) {
		getHibernateTemplate().update(person);
	}

	public void delete(Person person) {
		getHibernateTemplate().delete(person);
	}

	public void deleteAll() {
		getHibernateTemplate().deleteAll(readAll());
	}
}

Notice that this DAO implementation is extending from HibernateDaoSupport which is a Spring Hibernate convenience abstract class and is using its getHibernateTemplate() for all the data access operations. Also notice the hibernate query language (HQL) to express sql where conditions.

We’ll pack the property related files under src/main/resources grouped in meaningful folders maintaining a good housekeeping.

The Hibernate related DB mapping into is maintained under resources/hibernate/person.hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.dimitrisli.springHibernateMySQL.model.Person" table="PERSON_RECORD">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="string">
            <column name="NAME" length="15" not-null="true" unique="false" />
        </property>
        <property name="surname" type="string">
            <column name="SURNAME" length="15" not-null="true" unique="false" />
        </property>
    </class>
</hibernate-mapping>

MySQL related information like the driver name, the connection URL, the username/pass are found in the resources/properties/database.properties:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/javaTestDB
jdbc.username=javauser
jdbc.password=javapass

The database related beans are stored in resources/spring/database/database.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location">
		<value>properties/database.properties</value>
	</property>
</bean>

<bean id="dataSource"
         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="${jdbc.driverClassName}" />
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
</bean>

</beans>

Things to notice:

  • Spring’s PropertyPlaceholderConfigurer is conveniently reading the database properties and make them available to the datasource bean.

The Hibernate session factory represented in a LocalSessionFactoryBean is stored in the resources/spring/database/hibernate.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource">
      <ref bean="dataSource"/>
    </property>
    <property name="hibernateProperties">
       <props>
         <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
         <prop key="hibernate.show_sql">true</prop>
       </props>
     </property>
 	 <property name="mappingResources">
		<list>
           <value>/hibernate/person.hbm.xml</value>
		</list>
     </property>
    </bean>
</beans>

Note how the mapping config file is loaded as a property on the bean. Also how we are activating the HQL and show the output.

the final configuration file is gathering all the resources and found in resources/spring/config:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
	<import resource="../database/datasource.xml" />
	<import resource="../database/hibernate.xml" />
 	<import resource="../beans/beans.xml" />
 	
</beans>

Finally the test class that is creating the application context and making some basic demonstration queries:

package com.dimitrisli.springHibernateMySQL.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.dimitrisli.springHibernateMySQL.dao.PersonDaoHibernateImpl;
import com.dimitrisli.springHibernateMySQL.model.Person;

public class Main {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring/config/beanLocations.xml");
		PersonDaoHibernateImpl personDao = (PersonDaoHibernateImpl) context.getBean("personHibernateDao"); 
		
		System.out.println("Adding data...");
		Person dimitris = new Person(); dimitris.setName("dimitris"); dimitris.setSurname("liapis");
		Person michalis = new Person(); michalis.setName("michalis"); michalis.setSurname("liapis");
		Person kostas = new Person(); kostas.setName("kostas"); kostas.setSurname("liapis");
		Person stella = new Person(); stella.setName("stella"); stella.setSurname("liapis");
		personDao.create(dimitris);
		personDao.create(michalis);
		personDao.create(kostas);
		personDao.create(stella);
		
		System.out.println("Retrieving data..");
		for(Person currentPerson : personDao.readAll()){
			System.out.println("From DB: name="+currentPerson.getName()+", surname="+currentPerson.getSurname());
		}
		
		System.out.println("Deleting a record and viewing...");
		personDao.delete(kostas);
		for(Person currentPerson : personDao.readAll()){
			System.out.println("From DB: name="+currentPerson.getName()+", surname="+currentPerson.getSurname());
		}
		
		System.out.println("Updating a record and viewing...");
		dimitris.setName("newDimitrisName");
		personDao.update(dimitris);
		for(Person currentPerson : personDao.readAll()){
			System.out.println("From DB: name="+currentPerson.getName()+", surname="+currentPerson.getSurname());
		}
		
		System.out.println("Deleting everything and viewing...");
		personDao.deleteAll();
		for(Person currentPerson : personDao.readAll()){
			System.out.println("From DB: name="+currentPerson.getName()+", surname="+currentPerson.getSurname());
		}
		
	}
}

The output, subtracting the initialisation coding of the frameworks, looks like this:

Adding data...
Hibernate: insert into PERSON_RECORD (NAME, SURNAME) values (?, ?)
Hibernate: insert into PERSON_RECORD (NAME, SURNAME) values (?, ?)
Hibernate: insert into PERSON_RECORD (NAME, SURNAME) values (?, ?)
Hibernate: insert into PERSON_RECORD (NAME, SURNAME) values (?, ?)
Retrieving data..
Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_, person0_.SURNAME as SURNAME0_ from PERSON_RECORD person0_
From DB: name=newDimitrisName, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=stella, surname=liapis
From DB: name=dimitris, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=kostas, surname=liapis
From DB: name=stella, surname=liapis
Deleting a record and viewing...
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_, person0_.SURNAME as SURNAME0_ from PERSON_RECORD person0_
From DB: name=newDimitrisName, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=stella, surname=liapis
From DB: name=dimitris, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=stella, surname=liapis
Updating a record and viewing...
Hibernate: update PERSON_RECORD set NAME=?, SURNAME=? where ID=?
Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_, person0_.SURNAME as SURNAME0_ from PERSON_RECORD person0_
From DB: name=newDimitrisName, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=stella, surname=liapis
From DB: name=newDimitrisName, surname=liapis
From DB: name=michalis, surname=liapis
From DB: name=stella, surname=liapis
Deleting everything and viewing...
Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_, person0_.SURNAME as SURNAME0_ from PERSON_RECORD person0_
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: delete from PERSON_RECORD where ID=?
Hibernate: select person0_.ID as ID0_, person0_.NAME as NAME0_, person0_.SURNAME as SURNAME0_ from PERSON_RECORD person0_

The source code can be found in this Github repository.