Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 3527

Spring JDBC

$
0
0

I usually post about Persistence on Google+ - you can follow me there:

1. Overview

In this article we’ll go through practical usecases of the Spring JDBC module.

All the classes in Spring JDBC are divided into four separate packages:

  • core – the core functionality of JDBC. Some of the important classes under this package include JdbcTemplate, SimpleJdbcInsert, SimpleJdbcCall and NamedParameterJdbcTemplate.
  • datasource – utility classes to access datasource. It also has various datasource implementations that could be used for testing JDBC code outside the Java EE container.
  • object – DB access in an object oriented manner. It allows executing queries and returning the results as a business object. It maps the query results between the columns and properties of business objects.
  • support – support classes for classes under core and object packages. E.g. provides the SQLException translation functionality.

2. Configuration

Let’s start with some simple configuration of the datasource (we’ll use MySQL database for this example):

@Configuration
@ComponentScan("org.baeldung.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");

        return dataSource;
    }
}

Alternatively, you can also make good use of an embedded database for development or testing – here is a quick configuration that creates an instance of HSQL embedded database and pre-populates it with a simple SQL scripts:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("classpath:jdbc/schema.sql")
        .addScript("classpath:jdbc/test-data.sql").build();
}

Finally – the same can of course be done using XML configuring for the datasource:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
    <property name="username" value="guest_user"/>
    <property name="password" value="guest_password"/>
</bean>

3. The JdbcTemplate and running queries

3.1. Basic Queries

The JDBC template is the main API through which we’ll access most of the functionality that we’re interested in:

  • creation and closing of connections
  • executing statements and stored procedure calls
  • iterating over the ResultSet and returning results

Let’s start with a simple example to see what the JdbcTemplate can do:

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

And here’s a simple INSERT:

public int addEmplyee(int id) {
    return jdbcTemplate.update(
        "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", 5, "Bill", "Gates", "USA");
}

Notice the standard syntax of providing parameters – using the `?` character. Next – let’s look at an alternative to this syntax.

3.2. Queries with Named Parameters

To get support for named parameters, we’ll use the other JDBC template provided by the framework – the NamedParameterJdbcTemplate.

This wraps the JbdcTemplate and provides an alternative to the traditional syntax using “?” to specify parameters. Under the hood, it substitutes the named parameters to JDBC “?” placeholder and delegates to the wrapped JDCTemplate to execute the queries:

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
    "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

Notice how we are using the MapSqlParameterSource to provide the values for the named parameters.

Next – a quick example that uses properties from a bean to determine the named parameters:

Employee employee = new Employee();
employee.setFirstName("James");

String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(SELECT_BY_ID, namedParameters, Integer.class);

Note how we’re now making use of the BeanPropertySqlParameterSource implementations instead of specifying the named parameters manually like before.

3.3. Mapping Query Results to Java Object

Another very useful feature is the ability to map query results to java objects – by implementing RowMapper interface.

For example – for every row returned by the query, Spring uses the row mapper to populate the java bean:

public class EmployeeRowMapper implements RowMapper<Employee> {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();

        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));

        return employee;
    }
}

We can now pass the row mapper to the query API and get fully populated Java objects back:

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
List<Employee> employees = jdbcTemplate.queryForObject(
    query, new Object[] { id }, new EmployeeRowMapper());

4. Exception Translation

Spring comes with its own data exception hierarchy out of the box – with DataAccessException as the root exception – and it translates all underlying raw exceptions to it.

And so you keep your sanity by not having to handle low level persistence exceptions and benefit from the fact that Spring wraps the low level exceptions in DataAccessException or one of it’s sub-classes.

This keeps the exception handling mechanism independent of the underlying database you’re using.

Besides the default SQLErrorCodeSQLExceptionTranslator you can also provide your own implementation of SQLExceptionTranslator.

Here’s a quick example of a custom implementation, customizing the error message when there is integrity constraint violation:

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException customTranslate
      (String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == -104) {
            return new DuplicateKeyException(
                "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

To use this custom exception translator, we need to pass it to the JdbcTemplate by calling setExceptionTranslator() method:

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. JDBC operations using SimpleJdbc classes

SimpleJdbc classes provide easy way to configure and execute SQL statements. These classes use database metadata to build basic queries. SimpleJdbcInsert and SimpleJdbcCall classes provide easier way to execute insert and stored procedure calls.

5.1. SimpleJdbcInsert

Next – let’s take a look at executing simple insert statements with minimal configuration; the INSERT statement is generated based on the configuration of SimpleJdbcInsert.

All you need to provide is the Table name, Column names and values. Let’s first create the SimpleJdbcInsert:

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

Now, let’s provide the Column names and values, and execute the operation:

public int addEmplyee(Employee emp) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());

    return simpleJdbcInsert.execute(parameters);
}

To allow the database to generate the primary key, we can make use of the executeAndReturnKey() API; we’ll also need to configure the actual column that is auto-generated:

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");

Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

Finally – we can also pass in this data by using the BeanPropertySqlParameterSource and MapSqlParameterSource.

5.2. Stored Procedures with SimpleJdbcCall

Let’s now take a look at executing stored procedures – we’ll make use of the SimpleJdbcCall abstraction:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
		                     .withProcedureName("READ_EMPLOYEE");

public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map<String, Object> out = simpleJdbcCall.execute(in);

    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));

    return emp;
}

6. Batch operations

Another simple usecase – batching multiple operations together.

6.1. Basic batch operations using JdbcTemplate

Using JdbcTemplate, batch operations can be executed via the batchUpdate() API.

The interesting part here is the concise but highly useful BatchPreparedStatementSetter implementation:

public int[] batchUpdateUsingJdbcTemplate(List<Employee> employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. Batch operations using NamedParameterJdbcTemplate

You also have the option of batching operations with the NamedParameterJdbcTemplatebatchUpdate() API.

This API is simpler than the previous one – no need to implement any extra interfaces to set the parameters, as it has an internal prepared statement setter to set the parameter values.

Instead, the parameter values can be passed to the batchUpdate() method as an array of SqlParameterSource.

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. Conclusion

In this quick tutorial we looked at the JDBC abstraction in the Spring Framework, covering the various built capabilities provided by Spring JDBC with practical examples.

I usually post about Persistence on Google+ - you can follow me there:


Viewing all articles
Browse latest Browse all 3527

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>