com.wandrell.pattern.repository.spring.SpringJdbcRepository.java Source code

Java tutorial

Introduction

Here is the source code for com.wandrell.pattern.repository.spring.SpringJdbcRepository.java

Source

/**
 * The MIT License (MIT)
 * <p>
 * Copyright (c) 2015 the original author or authors.
 * <p>
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * <p>
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * <p>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.wandrell.pattern.repository.spring;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collection;

import javax.sql.DataSource;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.core.simple.SimpleJdbcInsertOperations;

import com.wandrell.pattern.query.NamedParameterQueryData;
import com.wandrell.pattern.repository.FilteredRepository;
import com.wandrell.pattern.repository.entity.PersistenceEntity;

/**
 * {@code FilteredRepository} for working with Spring's JDBC framework and Java
 * beans.
 * <p>
 * Entities are acquired with the use of templated SQL queries such as this:
 * <p>
 * {@code SELECT name FROM employees WHERE id = :id}
 * <p>
 * Where the {@code :id} placeholder will be swapped for an {@code id}
 * parameter. The use of named parameters instead of the {@code ?} placeholder
 * is thanks to Spring's classes.
 * <p>
 * For these queries a {@code NamedParameterQueryData} object will be received
 * by the repository. This will contain both the query to be used and the
 * parameters to apply.
 * <p>
 * When using the {@link #add(PersistenceEntity) add} and the
 * {@link #update(PersistenceEntity) update} methods it should be noted that
 * both will work the same. If the received entity lacks an identifier said
 * entity will be added into the database, otherwise the entity will be updated
 * in the data source.
 *
 * @author Bernardo Martnez Garrido
 * @param <V>
 *            the type stored on the repository
 * @see NamedParameterQueryData
 * @see PersistenceEntity
 */
public final class SpringJdbcRepository<V extends PersistenceEntity>
        implements FilteredRepository<V, NamedParameterQueryData> {

    /**
     * The class of the objects to be returned by the repository.
     * <p>
     * This is used by Spring's classes to transform query results.
     */
    private final Class<V> classType;

    /**
     * SQL query template for deleting entities.
     * <p>
     * This is a string template for generating delete SQL queries. For example,
     * it could be something like:
     * <p>
     * {@code DELETE FROM employees WHERE id = :id}
     * <p>
     * The use of named parameters instead of the {@code ?} placeholder is
     * thanks to Spring's classes.
     * <p>
     * The template will be used, along a received entity, to build and execute
     * the actual query.
     */
    private final String deleteQueryTemplate;

    /**
     * Insert operation handler.
     * <p>
     * This takes care of inserting entities into the database, and is generated
     * from the parameters received by the constructor.
     */
    private final SimpleJdbcInsertOperations insertHandler;

    /**
     * Named JDBC operations handler.
     * <p>
     * This takes care of the JDBC operations, allowing the use of named
     * parameters instead of the {@code ?} placeholder.
     */
    private final NamedParameterJdbcOperations jdbcTemplate;

    /**
     * SQL query for acquiring all the entities.
     * <p>
     * It will be generated from the parameters received by the constructor, and
     * would be something like:
     * <p>
     * {@code SELECT * FROM employees}
     */
    private final String selectAllQuery;

    /**
     * SQL query template for updating entities.
     * <p>
     * This is a string template for generating update SQL queries. For example,
     * it could be something like:
     * <p>
     * {@code UPDATE table SET column = :col WHERE id = :id}
     * <p>
     * The use of named parameters instead of the {@code ?} placeholder is
     * thanks to Spring's classes.
     * <p>
     * The template will be used, along a received entity, to build and execute
     * the actual query.
     */
    private final String updateQueryTemplate;

    /**
     * Constructs a {@code SpringJDBCRepository} with the specified data and
     * queries.
     * <p>
     * It will require templated queries for the update and delete operations.
     * The parameters for these queries will be acquired automatically from the
     * entity received for each of the operations.
     * <p>
     * The recommended delete query just requires knowing the ID of the entity,
     * so it can be similar to this:
     * <p>
     * {@code DELETE FROM employees WHERE id = :id"}
     * <p>
     * The update query requires all the columns which will be updated:
     * <p>
     * {@code UPDATE employees SET name = :name WHERE id = :id}
     * <p>
     * Any additional query which may be required, such as one for acquiring all
     * the entities, will be built from the received data.
     *
     * @param type
     *            the class of the objects to be returned
     * @param source
     *            source of the data
     * @param update
     *            query template for updating an entity on the database
     * @param delete
     *            query template for deleting an entity on the database
     * @param table
     *            table linked to the repository's entities
     * @param keys
     *            primary keys of the table
     */
    public SpringJdbcRepository(final Class<V> type, final DataSource source, final String update,
            final String delete, final String table, final String... keys) {
        super();

        checkNotNull(type, "Received a null pointer as the class type");
        checkNotNull(source, "Received a null pointer as the data source");
        checkNotNull(update, "Received a null pointer as the update query");
        checkNotNull(delete, "Received a null pointer as the delete query");
        checkNotNull(table, "Received a null pointer as the table");
        checkNotNull(keys, "Received a null pointer as the key columns");

        classType = type;

        // Queries
        selectAllQuery = String.format("SELECT * FROM %s", table);
        updateQueryTemplate = update;
        deleteQueryTemplate = delete;

        insertHandler = new SimpleJdbcInsert(source).withTableName(table).usingGeneratedKeyColumns(keys);

        jdbcTemplate = new NamedParameterJdbcTemplate(source);
    }

    /**
     * Constructs a {@code SpringJDBCRepository} with the specified data and
     * queries.
     * <p>
     * It will require templated queries for the update and delete operations.
     * The parameters for these queries will be acquired automatically from the
     * entity received for each of the operations.
     * <p>
     * The recommended delete query just requires knowing the ID of the entity,
     * so it can be similar to this:
     * <p>
     * {@code DELETE FROM employees WHERE id = :id"}
     * <p>
     * The update query requires all the columns which will be updated:
     * <p>
     * {@code UPDATE employees SET name = :name WHERE id = :id}
     * <p>
     * Any additional query which may be required, such as one for acquiring all
     * the entities, will be built from the received data.
     *
     * @param type
     *            the class of the objects to be returned
     * @param template
     *            JDBC template with access to the data
     * @param update
     *            query template for updating an entity on the database
     * @param delete
     *            query template for deleting an entity on the database
     * @param table
     *            table linked to the repository's entities
     * @param keys
     *            primary keys of the table
     */
    public SpringJdbcRepository(final Class<V> type, final JdbcTemplate template, final String update,
            final String delete, final String table, final String... keys) {
        super();

        checkNotNull(type, "Received a null pointer as the class type");
        checkNotNull(template, "Received a null pointer as the JDBC template");
        checkNotNull(update, "Received a null pointer as the update query");
        checkNotNull(delete, "Received a null pointer as the delete query");
        checkNotNull(table, "Received a null pointer as the table");
        checkNotNull(keys, "Received a null pointer as the key columns");

        classType = type;

        // Queries
        selectAllQuery = String.format("SELECT * FROM %s", table);
        updateQueryTemplate = update;
        deleteQueryTemplate = delete;

        insertHandler = new SimpleJdbcInsert(template).withTableName(table).usingGeneratedKeyColumns(keys);

        jdbcTemplate = new NamedParameterJdbcTemplate(template);
    }

    /**
     * Adds an entity to the repository, or updates it if it already exists.
     * <p>
     * Note that both the {@code add} and the {@link #update(PersistenceEntity)
     * update} methods work the same. If the entity does not exist it will be
     * added, but if it already exists then it will be updated.
     * <p>
     * If the entity is to be updated, then the update query received on the
     * constructor will be used.
     * <p>
     * If it is inserted, a query generated from the data received by the
     * constructor will be used.
     *
     * @param entity
     *            the entity to add
     */
    @Override
    public final void add(final V entity) {
        final SqlParameterSource parameterSource; // Parameters source
        final Number newKey; // Key assigned to the new
                             // entity

        checkNotNull(entity, "Received a null pointer as the entity");

        parameterSource = new BeanPropertySqlParameterSource(entity);

        if ((entity.getId() == null) || (entity.getId() < 0)) {
            // No ID has been assigned
            // It is a new entity
            newKey = getInsertHandler().executeAndReturnKey(parameterSource);

            entity.setId(newKey.intValue());
        } else {
            // ID already assigned
            // It is an existing entity
            getTemplate().update(getUpdateQueryTemplate(), parameterSource);
        }
    }

    /**
     * Returns all the entities contained in the repository.
     * <p>
     * The query used for this operation just queries the table received by the
     * constructor.
     *
     * @return all the entities contained in the repository
     */
    @Override
    public final Collection<V> getAll() {
        return getTemplate().query(getSelectAllValuesQuery(), BeanPropertyRowMapper.newInstance(getType()));
    }

    /**
     * Queries the entities in the repository and returns a subset of them.
     * <p>
     * The collection is created by building a query from the received
     * {@code QueryData} and executing it.
     *
     * @param query
     *            the query user to acquire the entities
     * @return the queried subset of entities
     */
    @Override
    public final Collection<V> getCollection(final NamedParameterQueryData query) {

        checkNotNull(query, "Received a null pointer as the query");

        return getTemplate().query(query.getQuery(), query.getParameters(),
                BeanPropertyRowMapper.newInstance(getType()));
    }

    /**
     * Queries the entities in the repository and returns a single one.
     * <p>
     * The entity is acquired by building a query from the received
     * {@code QueryData} and executing it.
     *
     * @param query
     *            the query user to acquire the entities
     * @return the queried entity
     */
    @Override
    public final V getEntity(final NamedParameterQueryData query) {
        V entity; // Entity acquired from the query

        checkNotNull(query, "Received a null pointer as the query");

        // Tries to acquire the entity
        try {
            entity = getTemplate().queryForObject(query.getQuery(), query.getParameters(),
                    BeanPropertyRowMapper.newInstance(getType()));
        } catch (final EmptyResultDataAccessException exception) {
            entity = null;
        }

        return entity;
    }

    /**
     * Removes an entity from the repository.
     * <p>
     * For this operation the delete query received on the constructor will be
     * used.
     *
     * @param entity
     *            the entity to remove
     */
    @Override
    public final void remove(final V entity) {
        final SqlParameterSource parameterSource; // Parameters source

        parameterSource = new BeanPropertySqlParameterSource(entity);

        getTemplate().update(getDeleteQueryTemplate(), parameterSource);
    }

    /**
     * Updates an entity on the repository, or adds it if missing.
     * <p>
     * Note that both the {@link #add(PersistenceEntity) add} and the
     * {@code update} methods work the same, as if the entity does not exist it
     * will be added, but if it already exists then it will be updated.
     * <p>
     * If the entity is to be updated, then the update query received on the
     * constructor will be used.
     * <p>
     * If it is inserted, a query generated from the data received by the
     * constructor will be used.
     *
     * @param entity
     *            the entity to add
     */
    @Override
    public final void update(final V entity) {
        add(entity);
    }

    /**
     * Returns the SQL query template used for deleting an entity.
     * <p>
     * Thanks to Spring's classes this query can make use of named parameters
     * such as this:
     * <p>
     * {@code DELETE FROM employees WHERE id = :id}
     *
     * @return the query template for deleting an entity
     */
    private final String getDeleteQueryTemplate() {
        return deleteQueryTemplate;
    }

    /**
     * Returns the handler of the insert operations.
     * <p>
     * This takes care of inserting entities into the database.
     *
     * @return the handler of the insert operations
     */
    private final SimpleJdbcInsertOperations getInsertHandler() {
        return insertHandler;
    }

    /**
     * Returns the query used for retrieving all the entities on the repository.
     *
     * @return the query for retrieving all the entities
     */
    private final String getSelectAllValuesQuery() {
        return selectAllQuery;
    }

    /**
     * Returns the template used for executing the queries.
     *
     * @return the template for executing the queries
     */
    private final NamedParameterJdbcOperations getTemplate() {
        return jdbcTemplate;
    }

    /**
     * Returns the class of the objects returned by the repository.
     * <p>
     * This is to be used when executing a query, to transform the query
     * results.
     *
     * @return the class of the objects returned by the repository
     */
    private final Class<V> getType() {
        return classType;
    }

    /**
     * Returns the query used for updating an entity.
     * <p>
     * Thanks to Spring's classes this query can make use of named parameters
     * such as this:
     * <p>
     * {@code UPDATE table SET column = :col WHERE id = :id}
     *
     * @return the query for updating an entity
     */
    private final String getUpdateQueryTemplate() {
        return updateQueryTemplate;
    }

}