Java tutorial
/** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/agpl.html> * * Copyright (C) Ushahidi Inc. All Rights Reserved. */ package com.ushahidi.swiftriver.core.api.dao.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; import com.ushahidi.swiftriver.core.api.dao.MediaDao; import com.ushahidi.swiftriver.core.api.dao.SequenceDao; import com.ushahidi.swiftriver.core.model.Drop; import com.ushahidi.swiftriver.core.model.Media; import com.ushahidi.swiftriver.core.model.MediaThumbnail; import com.ushahidi.swiftriver.core.model.Sequence; import com.ushahidi.swiftriver.core.util.MD5Util; /** * Repository class for Media * * @author ekala * */ @Repository public class JpaMediaDao extends AbstractJpaDao<Media> implements MediaDao { @Autowired private SequenceDao sequenceDao; private NamedParameterJdbcTemplate namedJdbcTemplate; private JdbcTemplate jdbcTemplate; @Autowired public void setDataSource(DataSource dataSource) { this.namedJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource); } /** * @see MediaDao#findByHash(ArrayList) */ @SuppressWarnings("unchecked") public List<Media> findByHash(ArrayList<String> mediaHashes) { String sql = "FROM Media WHERE hash IN (?1)"; return (List<Media>) em.createQuery(sql).setParameter(1, sql).getResultList(); } /** * Generate a hash for the given media. * * @param t * @return */ public String getHash(Media m) { return MD5Util.md5Hex(m.getUrl()); } @Override public void getMedia(List<Drop> drops) { // Get a lock Sequence seq = sequenceDao.findById("media"); Map<String, List<int[]>> newMediaIndex = getNewMediaIndex(drops); if (newMediaIndex.size() > 0) { // Find media that already exist in the db updateNewMediaIndex(newMediaIndex, drops); // Insert new media into the db batchInsert(newMediaIndex, drops, seq); } } /** * Generates a mapping of media hashes to list index for the given drops. * Also populates an media hash into the given list of drops. * * @param drops * @return */ private Map<String, List<int[]>> getNewMediaIndex(List<Drop> drops) { Map<String, List<int[]>> newMediaIndex = new HashMap<String, List<int[]>>(); // Generate hashes for each new drops i.e. those without an id for (int i = 0; i < drops.size(); i++) { Drop drop = drops.get(i); if (drop.getMedia() == null) continue; for (int j = 0; j < drop.getMedia().size(); j++) { Media media = drop.getMedia().get(j); // Cleanup the media media.setUrl(media.getUrl().trim()); String hash = getHash(media); media.setHash(hash); // Keep a record of where this hash is in the drop list List<int[]> indexes; if (newMediaIndex.containsKey(hash)) { indexes = newMediaIndex.get(hash); } else { indexes = new ArrayList<int[]>(); newMediaIndex.put(hash, indexes); } // Location of the media in the drops array is an i,j tuple int[] loc = { i, j }; indexes.add(loc); } } return newMediaIndex; } /** * For the given list of new drops, find those that the media hash already * exists in the db and update the drop entry with the existing id. Also * remove the hash from the new media index for those that already exist. * * @param newMediaIndex * @param drops */ private void updateNewMediaIndex(Map<String, List<int[]>> newMediaIndex, List<Drop> drops) { // First find and update existing drops with their ids. String sql = "SELECT id, hash FROM media WHERE hash IN (:hashes)"; MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("hashes", newMediaIndex.keySet()); List<Map<String, Object>> results = this.namedJdbcTemplate.queryForList(sql, params); // Update id for the drops that were found for (Map<String, Object> result : results) { String hash = (String) result.get("hash"); Long id = ((Number) result.get("id")).longValue(); List<int[]> indexes = newMediaIndex.get(hash); for (int[] index : indexes) { drops.get(index[0]).getMedia().get(index[1]).setId(id); } // Hash is not for a new drop so remove it newMediaIndex.remove(hash); } } /** * Insert new media in a single batch statement * * @param newMediaIndex * @param drops */ private void batchInsert(final Map<String, List<int[]>> newMediaIndex, final List<Drop> drops, Sequence seq) { final List<String> hashes = new ArrayList<String>(); hashes.addAll(newMediaIndex.keySet()); final long startKey = sequenceDao.getIds(seq, hashes.size()); String sql = "INSERT INTO media (id, hash, url, type) " + "VALUES (?,?,?,?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { String hash = hashes.get(i); // Update drops with the newly generated id for (int[] index : newMediaIndex.get(hash)) { drops.get(index[0]).getMedia().get(index[1]).setId(startKey + i); } int[] index = newMediaIndex.get(hash).get(0); Media media = drops.get(index[0]).getMedia().get(index[1]); ps.setLong(1, media.getId()); ps.setString(2, media.getHash()); ps.setString(3, media.getUrl()); ps.setString(4, media.getType()); } public int getBatchSize() { return hashes.size(); } }); // Media Thumbnails sql = "INSERT INTO media_thumbnails(`media_id`, `size`, `url`) VALUES(?, ?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { private int thumbnailCount = 0; public void setValues(PreparedStatement ps, int i) throws SQLException { String hash = hashes.get(i); int[] index = newMediaIndex.get(hash).get(0); Media media = drops.get(index[0]).getMedia().get(index[1]); for (MediaThumbnail thumbnail : media.getThumbnails()) { ps.setLong(1, media.getId()); ps.setInt(2, thumbnail.getSize()); ps.setString(3, thumbnail.getUrl()); thumbnailCount++; } } public int getBatchSize() { return thumbnailCount; } }); // Update the droplet_media table insertDropletMedia(drops); } /** * Populate the droplet media table. * * @param drops */ private void insertDropletMedia(List<Drop> drops) { // List of drop IDs in the drops list List<Long> dropIds = new ArrayList<Long>(); // List of media in a drop Map<Long, Set<Long>> dropletMediaMap = new HashMap<Long, Set<Long>>(); // List of drops and the media that is the drop image final List<long[]> dropImages = new ArrayList<long[]>(); for (Drop drop : drops) { if (drop.getMedia() == null) continue; dropIds.add(drop.getId()); for (Media media : drop.getMedia()) { Set<Long> m = null; if (dropletMediaMap.containsKey(drop.getId())) { m = dropletMediaMap.get(drop.getId()); } else { m = new HashSet<Long>(); dropletMediaMap.put(drop.getId(), m); } // Is this the drop image? if (drop.getImage() != null && media.getUrl().equals(drop.getImage().getUrl())) { long[] dropImage = { drop.getId(), media.getId() }; dropImages.add(dropImage); } m.add(media.getId()); } } // Find droplet media that already exist in the db String sql = "SELECT droplet_id, media_id FROM droplets_media WHERE droplet_id in (:ids)"; MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("ids", dropIds); List<Map<String, Object>> results = this.namedJdbcTemplate.queryForList(sql, params); // Remove already existing droplet_media from our Set for (Map<String, Object> result : results) { long dropletId = ((Number) result.get("droplet_id")).longValue(); long mediaId = ((Number) result.get("media_id")).longValue(); Set<Long> mediaSet = dropletMediaMap.get(dropletId); if (mediaSet != null) { mediaSet.remove(mediaId); } } // Insert the remaining items in the set into the db sql = "INSERT INTO droplets_media (droplet_id, media_id) VALUES (?,?)"; final List<long[]> dropletMediaList = new ArrayList<long[]>(); for (Long dropletId : dropletMediaMap.keySet()) { for (Long mediaId : dropletMediaMap.get(dropletId)) { long[] dropletMedia = { dropletId, mediaId }; dropletMediaList.add(dropletMedia); } } jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { long[] dropletMedia = dropletMediaList.get(i); ps.setLong(1, dropletMedia[0]); ps.setLong(2, dropletMedia[1]); } public int getBatchSize() { return dropletMediaList.size(); } }); if (dropImages.size() > 0) { sql = "UPDATE droplets SET droplet_image = ? WHERE id = ?"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { long[] update = dropImages.get(i); ps.setLong(1, update[1]); ps.setLong(2, update[0]); } public int getBatchSize() { return dropImages.size(); } }); } } }