// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.pgsimple.v0_6.impl; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.database.DbFeature; import org.openstreetmap.osmosis.core.domain.v0_6.Entity; import org.openstreetmap.osmosis.core.domain.v0_6.Tag; import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator; import org.openstreetmap.osmosis.pgsimple.common.BaseDao; import org.openstreetmap.osmosis.pgsimple.common.DatabaseContext; import org.openstreetmap.osmosis.pgsimple.common.NoSuchRecordException; /** * Provides functionality common to all top level entity daos. * * @author Brett Henderson * @param <T> * The entity type to be supported. */ public abstract class EntityDao<T extends Entity> extends BaseDao { private EntityFeatureDao<Tag, DbFeature<Tag>> tagDao; private ActionDao actionDao; private EntityMapper<T> entityMapper; private PreparedStatement countStatement; private PreparedStatement getStatement; private PreparedStatement insertStatement; private PreparedStatement updateStatement; private PreparedStatement deleteStatement; /** * Creates a new instance. * * @param dbCtx * The database context to use for accessing the database. * @param entityMapper * Provides entity type specific JDBC support. * @param actionDao * The dao to use for adding action records to the database. */ protected EntityDao(DatabaseContext dbCtx, EntityMapper<T> entityMapper, ActionDao actionDao) { super(dbCtx); this.entityMapper = entityMapper; this.actionDao = actionDao; tagDao = new EntityFeatureDao<Tag, DbFeature<Tag>>(dbCtx, new TagMapper(entityMapper.getEntityName())); } /** * Checks if the specified entity exists in the database. * * @param entityId * The unique identifier of the entity. * @return True if the entity exists in the database. */ public boolean exists(long entityId) { if (countStatement == null) { countStatement = prepareStatement(entityMapper.getSqlSelectCount(true)); } try { countStatement.setLong(1, entityId); try (ResultSet resultSet = countStatement.executeQuery()) { boolean result; if (!resultSet.next()) { throw new OsmosisRuntimeException( "Entity count query didn't return any rows."); } result = resultSet.getLong("count") > 0; return result; } } catch (SQLException e) { throw new OsmosisRuntimeException( "Count query failed for " + entityMapper.getEntityName() + " " + entityId + ".", e ); } } /** * Loads the specified entity from the database. * * @param entityId * The unique identifier of the entity. * @return The loaded entity. */ public T getEntity(long entityId) { T entity; if (getStatement == null) { getStatement = prepareStatement(entityMapper.getSqlSelect(true, true)); } try { getStatement.setLong(1, entityId); try (ResultSet resultSet = getStatement.executeQuery()) { if (!resultSet.next()) { throw new NoSuchRecordException(entityMapper.getEntityName() + " " + entityId + " doesn't exist."); } entity = entityMapper.parseRecord(resultSet); } for (DbFeature<Tag> dbTag : tagDao.getAll(entityId)) { entity.getTags().add(dbTag.getFeature()); } // Add the type specific features. loadFeatures(entityId, entity); return entity; } catch (SQLException e) { throw new OsmosisRuntimeException( "Query failed for " + entityMapper.getEntityName() + " " + entityId + ".", e ); } } /** * Adds the specified tags to the database. * * @param entityId * The identifier of the entity to add these features to. * @param tags * The features to add. */ private void addTags(long entityId, Collection<Tag> tags) { Collection<DbFeature<Tag>> dbList; dbList = new ArrayList<DbFeature<Tag>>(tags.size()); for (Tag tag : tags) { dbList.add(new DbFeature<Tag>(entityId, tag)); } tagDao.addAll(dbList); } /** * Adds the type specific features to the entity. * * @param entityId * The entity id. * @param entity * The entity requiring features to be added. */ protected abstract void loadFeatures(long entityId, T entity); /** * Adds the specified entity to the database. * * @param entity * The entity to add. */ public void addEntity(T entity) { if (insertStatement == null) { insertStatement = prepareStatement(entityMapper.getSqlInsert(1)); } try { entityMapper.populateEntityParameters(insertStatement, 1, entity); insertStatement.executeUpdate(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Insert failed for " + entityMapper.getEntityName() + " " + entity.getId() + ".", e ); } addTags(entity.getId(), entity.getTags()); actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.CREATE, entity.getId()); } /** * Updates the specified entity details in the database. * * @param entity * The entity to update. */ public void modifyEntity(T entity) { if (updateStatement == null) { updateStatement = prepareStatement(entityMapper.getSqlUpdate(true)); } try { int prmIndex; prmIndex = 1; prmIndex = entityMapper.populateEntityParameters(updateStatement, prmIndex, entity); updateStatement.setLong(prmIndex++, entity.getId()); updateStatement.executeUpdate(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Update failed for " + entityMapper.getEntityName() + " " + entity.getId() + ".", e ); } tagDao.removeList(entity.getId()); addTags(entity.getId(), entity.getTags()); actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.MODIFY, entity.getId()); } /** * Removes the specified entity from the database. * * @param entityId * The id of the entity to remove. */ public void removeEntity(long entityId) { int prmIndex; tagDao.removeList(entityId); if (deleteStatement == null) { deleteStatement = prepareStatement(entityMapper.getSqlDelete(true)); } try { prmIndex = 1; deleteStatement.setLong(prmIndex++, entityId); deleteStatement.executeUpdate(); } catch (SQLException e) { throw new OsmosisRuntimeException( "Delete failed for " + entityMapper.getEntityName() + " " + entityId + ".", e ); } actionDao.addAction(entityMapper.getEntityType(), ChangesetAction.DELETE, entityId); } /** * Returns an iterator providing access to all entities in the database. * * @return The entity iterator. */ public abstract ReleasableIterator<T> iterate(); }