package io.kaif.model.zone; import static java.util.stream.Collectors.*; import java.sql.Timestamp; import java.util.List; import java.util.Optional; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; import io.kaif.database.DaoOperations; import io.kaif.model.account.Authority; /** * @see io.kaif.config.UtilConfiguration#zoneInfoCacheManager() */ @Repository @CacheConfig(cacheNames = "ZoneInfo") public class ZoneDao implements DaoOperations { @Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final RowMapper<ZoneInfo> zoneInfoMapper = (rs, n) -> { List<UUID> adminAccountIds = convertUuidArray(rs.getArray("adminAccountIds")).collect(toList()); return new ZoneInfo(// Zone.valueOf(rs.getString("zone")), rs.getString("aliasName"), rs.getString("theme"), Authority.valueOf(rs.getString("voteAuthority")), Authority.valueOf(rs.getString("debateAuthority")), Authority.valueOf(rs.getString("writeAuthority")), adminAccountIds, rs.getBoolean("hideFromTop"), rs.getTimestamp("createTime").toInstant()); }; public RowMapper<ZoneInfo> getZoneInfoMapper() { return zoneInfoMapper; } @Override public NamedParameterJdbcTemplate namedJdbc() { return namedParameterJdbcTemplate; } @CacheEvict(value = "listAdministrators", key = "#a0.zone") public ZoneInfo create(ZoneInfo zoneInfo) { jdbc().update("" + " INSERT " + " INTO ZoneInfo " + " (zone, aliasName, theme, voteAuthority, debateAuthority, writeAuthority, " + " createTime, adminAccountIds, hideFromTop) " + " VALUES " + questions(9), zoneInfo.getZone().value(), zoneInfo.getAliasName(), zoneInfo.getTheme(), zoneInfo.getVoteAuthority().name(), zoneInfo.getDebateAuthority().name(), zoneInfo.getWriteAuthority().name(), Timestamp.from(zoneInfo.getCreateTime()), createUuidArray(zoneInfo.getAdminAccountIds().stream()), zoneInfo.isHideFromTop()); createZoneAdmin(zoneInfo); return zoneInfo; } private void createZoneAdmin(ZoneInfo zoneInfo) { List<Object[]> batchArgs = zoneInfo.getAdminAccountIds() .stream() .map(uuid -> new Object[] { uuid, zoneInfo.getName(), Timestamp.from(zoneInfo.getCreateTime()) }) .collect(toList()); jdbc().batchUpdate("" + " INSERT " + " INTO ZoneAdmin " + " (accountId, zone, createTime) " + " VALUES " + questions(3), batchArgs); } public ZoneInfo loadZoneWithoutCache(Zone zone) throws EmptyResultDataAccessException { return jdbc().queryForObject("SELECT * FROM ZoneInfo WHERE zone = ? ", zoneInfoMapper, zone.value()); } public Optional<ZoneInfo> findZoneWithoutCache(Zone zone) { final String sql = " SELECT * FROM ZoneInfo WHERE zone = ? LIMIT 1 "; return jdbc().query(sql, zoneInfoMapper, zone.value()).stream().findAny(); } //use argument `zone` as cache key @Cacheable public ZoneInfo loadZoneWithCache(Zone zone) throws EmptyResultDataAccessException { return loadZoneWithoutCache(zone); } @CacheEvict(key = "#a0") //a0 is first argument public void updateTheme(Zone zone, String theme) { jdbc().update("UPDATE ZoneInfo SET theme = ? WHERE zone = ? ", theme, zone.value()); } public List<ZoneInfo> listOrderByName() { //do we need cache ? the data size is small enough, db should do well return jdbc().query(" SELECT * FROM ZoneInfo ORDER BY zone ", zoneInfoMapper); } public List<ZoneInfo> listZonesByAdmin(UUID accountId) { return jdbc().query("" + " SELECT ZoneInfo.* " + " FROM ZoneAdmin " + " LEFT OUTER JOIN ZoneInfo ON (ZoneAdmin.zone = ZoneInfo.zone) " + " WHERE accountId = ? " + " ORDER BY ZoneInfo.zone ", zoneInfoMapper, accountId); } public boolean isZoneAdmin(Zone zone, UUID accountId) { return jdbc().queryForObject("" + " SELECT count(*) > 0 " + " FROM ZoneAdmin " + " WHERE accountId = ? " + " AND zone = ? ", Boolean.class, accountId, zone.value()); } @Cacheable("listAdministrators") public List<String> listAdministratorsWithCache(Zone zone) { //TODO evict if we can add new administrators return jdbc().query("" + " SELECT Account.username " + " FROM Account " + " JOIN ZoneAdmin ON (ZoneAdmin.accountId = Account.accountId) " + " WHERE zone = ? " + " ORDER BY Account.username ", (rs, n) -> rs.getString("username"), zone.value()); } }