Java tutorial
/* * 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; } } } }