org.vader.apm.dao.impl.ProfileDaoImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.vader.apm.dao.impl.ProfileDaoImpl.java

Source

/*
 * Copyright 2014. Vadim Baranov
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.vader.apm.dao.impl;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.stereotype.Repository;
import org.vader.apm.dao.ProfileDao;
import org.vader.apm.dao.util.DaoUtils;
import org.vader.apm.dao.util.DbObjectState;
import org.vader.apm.dao.util.MapExtractor;
import org.vader.apm.domain.Address;
import org.vader.apm.domain.Profile;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author Vadim Baranov
 */
@Repository
public class ProfileDaoImpl extends NamedParameterJdbcDaoSupport implements ProfileDao {
    @Override
    @CachePut(value = { "PROFILES_L1", "PROFILES_L2" }, key = "#result.id")
    public Profile insert(Profile profile) {
        DaoUtils.assertObjectState(DbObjectState.TRANSIENT, profile);

        final Long id = getJdbcTemplate().queryForObject(
                "INSERT INTO profiles (id, name, surname) VALUES (nextval('profiles_seq'), ?, ?) RETURNING id",
                Long.class, profile.getName(), profile.getSurName());
        profile.setId(id);

        updateProfilePhones(profile);
        insertAddresses(DaoUtils.getDbObjectsByState(profile.getAddresses().values(), DbObjectState.TRANSIENT));
        updateAddresses(DaoUtils.getDbObjectsByState(profile.getAddresses().values(), DbObjectState.PERSISTENT));
        updateProfileAddresses(profile);
        return profile;
    }

    @Override
    @Cacheable(value = { "PROFILES_L1", "PROFILES_L2" }, key = "#id")
    public Profile getById(long id) {
        try {
            final Profile result = getJdbcTemplate().queryForObject("SELECT * FROM profiles WHERE id = ?",
                    ProfileRowMapper.INSTANCE, id);

            final MapExtractor<String, Address> addressExtractor = new MapExtractor<>("address_type", String.class,
                    AddressRowMapper.INSTANCE);
            getJdbcTemplate().query(
                    "SELECT pa.type address_type, ad.* FROM profile_addresses pa "
                            + "INNER JOIN addresses ad ON (pa.address_id = ad.id) WHERE pa.profile_id = ?",
                    addressExtractor, id);
            result.setAddresses(addressExtractor.getResult());

            final MapExtractor<String, String> phoneExtractor = new MapExtractor<>("type", String.class, "value",
                    String.class);
            getJdbcTemplate().query("SELECT type, value FROM profile_phones WHERE profile_id = ?", phoneExtractor,
                    id);
            result.setPhones(phoneExtractor.getResult());

            return result;
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
    }

    @Override
    @CachePut(value = { "PROFILES_L1", "PROFILES_L2" }, key = "#result.id")
    public Profile update(Profile profile) {
        DaoUtils.assertObjectState(DbObjectState.PERSISTENT, profile);
        DaoUtils.assertOne(getJdbcTemplate().update("UPDATE profiles SET name = ?, surname = ? WHERE id = ?",
                Long.class, profile.getName(), profile.getSurName(), profile.getId()));

        updateProfilePhones(profile);
        insertAddresses(DaoUtils.getDbObjectsByState(profile.getAddresses().values(), DbObjectState.TRANSIENT));
        updateAddresses(DaoUtils.getDbObjectsByState(profile.getAddresses().values(), DbObjectState.PERSISTENT));
        updateProfileAddresses(profile);
        return profile;
    }

    @Override
    @CacheEvict(value = { "PROFILES_L1", "PROFILES_L2" }, key = "#id")
    public void delete(long id) {
        DaoUtils.assertOne(getJdbcTemplate().update("DELETE FROM profiles WHERE id = ?", id));
    }

    private void updateProfilePhones(Profile profile) {
        getJdbcTemplate().update("DELETE FROM profile_phones WHERE profile_id = ?", profile.getId());

        final List<Object[]> args = new ArrayList<>(profile.getPhones().size());
        for (Map.Entry<String, String> phoneEntry : profile.getPhones().entrySet()) {
            args.add(new Object[] { profile.getId(), phoneEntry.getKey(), phoneEntry.getValue() });
        }
        getJdbcTemplate().batchUpdate("INSERT INTO profile_phones (profile_id, type, value) VALUES (?, ?, ?)",
                args);
    }

    private void insertAddresses(Collection<Address> addresses) {
        if (!addresses.isEmpty()) {
            final List<Long> ids = DaoUtils.getSeqNextNVal("addresses_seq", addresses.size(), getJdbcTemplate());
            final List<Object[]> args = new ArrayList<>(addresses.size());
            int i = 0;
            for (Address address : addresses) {
                address.setId(ids.get(i++));
                args.add(new Object[] { address.getId(), address.getCity(), address.getStreet() });
            }
            getJdbcTemplate().batchUpdate("INSERT INTO addresses (id, city, street) VALUES (?, ?, ?)", args);
        }
    }

    private void updateAddresses(Collection<Address> addresses) {
        if (!addresses.isEmpty()) {
            final List<Object[]> args = new ArrayList<>(addresses.size());
            for (Address address : addresses) {
                args.add(new Object[] { address.getCity(), address.getStreet(), address.getId() });
            }
            getJdbcTemplate().batchUpdate("UPDATE addresses SET city = ?, street = ? WHERE id = ?", args);
        }
    }

    private void updateProfileAddresses(Profile profile) {
        getJdbcTemplate().update("DELETE FROM profile_addresses WHERE profile_id = ?", profile.getId());
        final List<Object[]> args = new ArrayList<>(profile.getAddresses().size());
        for (Map.Entry<String, Address> addressEntry : profile.getAddresses().entrySet()) {
            args.add(new Object[] { profile.getId(), addressEntry.getKey(), addressEntry.getValue().getId() });
        }
        getJdbcTemplate()
                .batchUpdate("INSERT INTO profile_addresses (profile_id, type, address_id) VALUES (?, ?, ?)", args);
    }

    /**
     * Profile row mapper.
     */
    private enum ProfileRowMapper implements RowMapper<Profile> {
        INSTANCE {
            @Override
            public Profile mapRow(ResultSet rs, int rowNum) throws SQLException {
                final Profile result = new Profile();
                result.setId(rs.getLong("id"));
                result.setName(rs.getString("name"));
                result.setSurName(rs.getString("surname"));
                return result;
            }
        }
    }

    /**
     * Address row mapper.
     */
    private enum AddressRowMapper implements RowMapper<Address> {
        INSTANCE {
            @Override
            public Address mapRow(ResultSet rs, int rowNum) throws SQLException {
                final Address result = new Address();
                result.setId(rs.getLong("id"));
                result.setCity(rs.getString("city"));
                result.setStreet(rs.getString("street"));
                return result;
            }
        }
    }
}