diff --git a/src/main/java/alfio/manager/AdminReservationManager.java b/src/main/java/alfio/manager/AdminReservationManager.java index 706edecac2..44dbabc358 100644 --- a/src/main/java/alfio/manager/AdminReservationManager.java +++ b/src/main/java/alfio/manager/AdminReservationManager.java @@ -49,7 +49,6 @@ import org.springframework.context.MessageSource; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -88,7 +87,6 @@ public class AdminReservationManager { private final TicketReservationManager ticketReservationManager; private final TicketCategoryRepository ticketCategoryRepository; private final TicketRepository ticketRepository; - private final NamedParameterJdbcTemplate jdbc; private final SpecialPriceRepository specialPriceRepository; private final TicketReservationRepository ticketReservationRepository; private final EventRepository eventRepository; @@ -552,7 +550,7 @@ private Result checkExistingCategory(TicketsInfo ti, Event event private void createMissingTickets(Event event, int tickets) { final MapSqlParameterSource[] params = generateEmptyTickets(event, Date.from(ZonedDateTime.now(event.getZoneId()).toInstant()), tickets, Ticket.TicketStatus.FREE).toArray(MapSqlParameterSource[]::new); - jdbc.batchUpdate(ticketRepository.bulkTicketInitialization(), params); + ticketRepository.bulkTicketInitialization(params); } @Transactional @@ -696,15 +694,11 @@ private void removeTicketsFromReservation(TicketReservation reservation, Event e ticketRepository.resetCategoryIdForUnboundedCategoriesWithTicketIds(ticketIds); ticketFieldRepository.deleteAllValuesForTicketIds(ticketIds); - MapSqlParameterSource[] args = ticketIds.stream().map(id -> new MapSqlParameterSource("ticketId", id) - .addValue("reservationId", reservationId) - .addValue("eventId", event.getId()) - .addValue("newUuid", UUID.randomUUID().toString()) - ).toArray(MapSqlParameterSource[]::new); + List reservationIds = ticketRepository.findReservationIds(ticketIds); List ticketUUIDs = ticketRepository.findUUIDs(ticketIds); - int[] results = jdbc.batchUpdate(ticketRepository.batchReleaseTickets(), args); - Validate.isTrue(Arrays.stream(results).sum() == args.length, "Failed to update tickets"); + int[] results = ticketRepository.batchReleaseTickets(reservationId, ticketIds, event); + Validate.isTrue(Arrays.stream(results).sum() == ticketIds.size(), "Failed to update tickets"); if(!removeReservation) { if(forceInvoiceReceiptUpdate) { auditingRepository.insert(reservationId, userId, event.getId(), FORCED_UPDATE_INVOICE, date, RESERVATION, reservationId); diff --git a/src/main/java/alfio/manager/AdminReservationRequestManager.java b/src/main/java/alfio/manager/AdminReservationRequestManager.java index ff188b2b1b..3821c1c910 100644 --- a/src/main/java/alfio/manager/AdminReservationRequestManager.java +++ b/src/main/java/alfio/manager/AdminReservationRequestManager.java @@ -25,7 +25,6 @@ import alfio.repository.AdminReservationRequestRepository; import alfio.repository.EventRepository; import alfio.repository.user.UserRepository; -import alfio.util.Json; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.apache.commons.collections.CollectionUtils; @@ -33,7 +32,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -47,7 +45,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static alfio.util.OptionalWrapper.optionally; import static java.util.Collections.singletonList; import static java.util.Optional.ofNullable; @@ -59,7 +56,6 @@ public class AdminReservationRequestManager { private final AdminReservationManager adminReservationManager; private final EventManager eventManager; - private final NamedParameterJdbcTemplate jdbc; private final UserRepository userRepository; private final AdminReservationRequestRepository adminReservationRequestRepository; private final EventRepository eventRepository; @@ -90,6 +86,7 @@ public Result scheduleReservations(String eventName, .orElseGet(() -> Result.error(ErrorCode.ReservationError.UPDATE_FAILED)); } + //TODO: rewrite, and add test! public Pair processPendingReservations() { Map> result = adminReservationRequestRepository.findPendingForUpdate(1000) .stream() @@ -98,13 +95,13 @@ public Pair processPendingReservations() { Result, Event>> reservationResult = Result.fromNullable(eventRepository.findOptionalById((int) request.getEventId()).orElse(null), ErrorCode.EventError.NOT_FOUND) .flatMap(e -> Result.fromNullable(userRepository.findOptionalById((int) request.getUserId()).map(u -> Pair.of(e, u)).orElse(null), ErrorCode.EventError.ACCESS_DENIED)) - .flatMap(p -> processReservation(request, p)); + .flatMap(p -> processReservation(request, p.getLeft(), p.getRight())); return buildParameterSource(id, reservationResult); }).collect(Collectors.partitioningBy(ps -> AdminReservationRequest.Status.SUCCESS.name().equals(ps.getValue("status")))); result.values().forEach(list -> { try { - jdbc.batchUpdate(adminReservationRequestRepository.updateStatus(), list.toArray(new MapSqlParameterSource[0])); + adminReservationRequestRepository.updateStatus(list); } catch(Exception e) { log.fatal("cannot update the status of "+list.size()+" reservations", e); } @@ -114,14 +111,14 @@ public Pair processPendingReservations() { } - private Result, Event>> processReservation(AdminReservationRequest request, Pair p) { + private Result, Event>> processReservation(AdminReservationRequest request, Event event, User user) { DefaultTransactionDefinition definition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NESTED); TransactionTemplate template = new TransactionTemplate(transactionManager, definition); return template.execute(status -> { var savepoint = status.createSavepoint(); try { - String eventName = p.getLeft().getShortName(); - String username = p.getRight().getUsername(); + String eventName = event.getShortName(); + String username = user.getUsername(); Result, Event>> result = adminReservationManager.createReservation(request.getBody(), eventName, username) .flatMap(r -> adminReservationManager.confirmReservation(eventName, r.getLeft().getId(), username)); if(!result.isSuccess()) { @@ -147,15 +144,7 @@ private Result insertRequest(AdminReservationModification body, EventAnd try { String requestId = UUID.randomUUID().toString(); long userId = userRepository.findIdByUserName(username).orElseThrow(IllegalArgumentException::new); - MapSqlParameterSource[] requests = spread(body, singleReservation) - .map(res -> new MapSqlParameterSource("userId", userId) - .addValue("requestId", requestId) - .addValue("requestType", AdminReservationRequest.RequestType.IMPORT.name()) - .addValue("status", AdminReservationRequest.Status.PENDING.name()) - .addValue("eventId", event.getId()) - .addValue("body", Json.toJson(res))) - .toArray(MapSqlParameterSource[]::new); - jdbc.batchUpdate(adminReservationRequestRepository.insertRequest(), requests); + adminReservationRequestRepository.insertRequest(requestId, userId, event, spread(body, singleReservation)); return Result.success(requestId); } catch (Exception e) { log.error("can't insert reservation request", e); diff --git a/src/main/java/alfio/manager/EventManager.java b/src/main/java/alfio/manager/EventManager.java index 9aa5ce50b9..f2f785aac2 100644 --- a/src/main/java/alfio/manager/EventManager.java +++ b/src/main/java/alfio/manager/EventManager.java @@ -51,7 +51,6 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -91,7 +90,6 @@ public class EventManager { private final TicketRepository ticketRepository; private final SpecialPriceRepository specialPriceRepository; private final PromoCodeDiscountRepository promoCodeRepository; - private final NamedParameterJdbcTemplate jdbc; private final ConfigurationManager configurationManager; private final TicketFieldRepository ticketFieldRepository; private final EventDeleterRepository eventDeleterRepository; @@ -339,7 +337,7 @@ public void updateEventPrices(EventAndOrganizationId original, EventModification Event modified = eventRepository.findById(eventId); if(seatsDifference > 0) { final MapSqlParameterSource[] params = generateEmptyTickets(modified, Date.from(ZonedDateTime.now(modified.getZoneId()).toInstant()), seatsDifference, TicketStatus.RELEASED).toArray(MapSqlParameterSource[]::new); - jdbc.batchUpdate(ticketRepository.bulkTicketInitialization(), params); + ticketRepository.bulkTicketInitialization(params); } else { List ids = ticketRepository.selectNotAllocatedTicketsForUpdate(eventId, Math.abs(seatsDifference), singletonList(TicketStatus.FREE.name())); Validate.isTrue(ids.size() == Math.abs(seatsDifference), "cannot lock enough tickets for deletion."); @@ -552,8 +550,7 @@ private void createCategoriesForEvent(EventModification em, Event event) { if (tc.isTokenGenerationRequested()) { final TicketCategory ticketCategory = ticketCategoryRepository.getByIdAndActive(category.getKey(), event.getId()); - final MapSqlParameterSource[] args = prepareTokenBulkInsertParameters(ticketCategory, ticketCategory.getMaxTickets()); - jdbc.batchUpdate(specialPriceRepository.bulkInsert(), args); + specialPriceRepository.bulkInsert(ticketCategory, ticketCategory.getMaxTickets()); } }); } @@ -571,7 +568,7 @@ private Integer insertCategory(TicketCategoryModification tc, Event event) { TicketCategory ticketCategory = ticketCategoryRepository.getByIdAndActive(category.getKey(), eventId); if(tc.isBounded()) { List lockedTickets = ticketRepository.selectNotAllocatedTicketsForUpdate(eventId, ticketCategory.getMaxTickets(), asList(TicketStatus.FREE.name(), TicketStatus.RELEASED.name())); - jdbc.batchUpdate(ticketRepository.bulkTicketUpdate(), lockedTickets.stream().map(id -> new MapSqlParameterSource("id", id).addValue("categoryId", ticketCategory.getId()).addValue("srcPriceCts", ticketCategory.getSrcPriceCts())).toArray(MapSqlParameterSource[]::new)); + ticketRepository.bulkTicketUpdate(lockedTickets, ticketCategory); if(tc.isTokenGenerationRequested()) { insertTokens(ticketCategory); ticketRepository.revertToFree(eventId, ticketCategory.getId(), lockedTickets); @@ -589,8 +586,7 @@ private void insertTokens(TicketCategory ticketCategory) { } private void insertTokens(TicketCategory ticketCategory, int requiredTokens) { - final MapSqlParameterSource[] args = prepareTokenBulkInsertParameters(ticketCategory, requiredTokens); - jdbc.batchUpdate(specialPriceRepository.bulkInsert(), args); + specialPriceRepository.bulkInsert(ticketCategory, requiredTokens); } private void insertOrUpdateTicketCategoryDescription(int tcId, TicketCategoryModification tc, Event event) { @@ -672,14 +668,13 @@ void handlePriceChange(EventAndOrganizationId event, TicketCategory original, Ti void handleTokenModification(TicketCategory original, TicketCategory updated, int addedTickets) { if(original.isAccessRestricted() ^ updated.isAccessRestricted()) { if(updated.isAccessRestricted()) { - final MapSqlParameterSource[] args = prepareTokenBulkInsertParameters(updated, updated.getMaxTickets()); - jdbc.batchUpdate(specialPriceRepository.bulkInsert(), args); + specialPriceRepository.bulkInsert(updated, updated.getMaxTickets()); } else { specialPriceRepository.cancelExpiredTokens(updated.getId()); } } else if(updated.isAccessRestricted() && addedTickets != 0) { if(addedTickets > 0) { - jdbc.batchUpdate(specialPriceRepository.bulkInsert(), prepareTokenBulkInsertParameters(updated, addedTickets)); + specialPriceRepository.bulkInsert(updated, addedTickets); } else { int absDifference = Math.abs(addedTickets); final List ids = specialPriceRepository.lockNotSentTokens(updated.getId(), absDifference); @@ -702,9 +697,7 @@ void handleTicketNumberModification(Event event, TicketCategory original, Ticket //the updated category contains more tickets than the older one List lockedTickets = ticketRepository.selectNotAllocatedTicketsForUpdate(event.getId(), addedTickets, asList(TicketStatus.FREE.name(), TicketStatus.RELEASED.name())); Validate.isTrue(addedTickets == lockedTickets.size(), "Cannot add %d tickets. There are only %d free tickets.", addedTickets, lockedTickets.size()); - jdbc.batchUpdate(ticketRepository.bulkTicketUpdate(), lockedTickets.stream() - .map(id -> new MapSqlParameterSource("id", id).addValue("categoryId", updated.getId()).addValue("srcPriceCts", updated.getSrcPriceCts())) - .toArray(MapSqlParameterSource[]::new)); + ticketRepository.bulkTicketUpdate(lockedTickets, updated); if(updated.isAccessRestricted()) { //since the updated category is not public, the tickets shouldn't be distributed to waiting people. ticketRepository.revertToFree(event.getId(), updated.getId(), lockedTickets); @@ -721,25 +714,14 @@ void handleTicketNumberModification(Event event, TicketCategory original, Ticket } ticketRepository.invalidateTickets(ids); final MapSqlParameterSource[] params = generateEmptyTickets(event, Date.from(ZonedDateTime.now(event.getZoneId()).toInstant()), absDifference, TicketStatus.RELEASED).toArray(MapSqlParameterSource[]::new); - jdbc.batchUpdate(ticketRepository.bulkTicketInitialization(), params); + ticketRepository.bulkTicketInitialization(params); } } - private MapSqlParameterSource[] prepareTokenBulkInsertParameters(TicketCategory tc, int limit) { - return generateStreamForTicketCreation(limit) - .peek(ps -> { - ps.addValue("code", UUID.randomUUID().toString()); - ps.addValue("priceInCents", tc.getSrcPriceCts()); - ps.addValue("ticketCategoryId", tc.getId()); - ps.addValue("status", SpecialPrice.Status.WAITING.name()); - }) - .toArray(MapSqlParameterSource[]::new); - } - private void createAllTicketsForEvent(Event event, EventModification em) { Validate.notNull(em.getAvailableSeats()); final MapSqlParameterSource[] params = prepareTicketsBulkInsertParameters(ZonedDateTime.now(event.getZoneId()), event, em.getAvailableSeats(), TicketStatus.FREE); - jdbc.batchUpdate(ticketRepository.bulkTicketInitialization(), params); + ticketRepository.bulkTicketInitialization(params); } private int insertEvent(EventModification em) { diff --git a/src/main/java/alfio/manager/FileUploadManager.java b/src/main/java/alfio/manager/FileUploadManager.java index fc4935fbb3..01af90f737 100644 --- a/src/main/java/alfio/manager/FileUploadManager.java +++ b/src/main/java/alfio/manager/FileUploadManager.java @@ -19,7 +19,6 @@ import alfio.model.FileBlobMetadata; import alfio.model.modification.UploadBase64FileModification; import alfio.repository.FileUploadRepository; -import alfio.util.Json; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import lombok.RequiredArgsConstructor; @@ -27,13 +26,6 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; -import org.springframework.jdbc.support.lob.DefaultLobHandler; -import org.springframework.jdbc.support.lob.LobCreator; -import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StreamUtils; @@ -41,8 +33,6 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.*; import java.util.concurrent.TimeUnit; @@ -56,7 +46,6 @@ public class FileUploadManager { * Maximum allowed file size is 200kb */ static final int MAXIMUM_ALLOWED_SIZE = 1024 * 200; - private final NamedParameterJdbcTemplate jdbc; private final FileUploadRepository repository; private final Cache cache = Caffeine.newBuilder() .maximumSize(20) @@ -68,18 +57,7 @@ public Optional findMetadata(String id) { } public void outputFile(String id, OutputStream out) { - byte[] res = cache.get(id, identifier -> { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SqlParameterSource param = new MapSqlParameterSource("id", id); - jdbc.query(repository.fileContent(id), param, rs -> { - try (InputStream is = rs.getBinaryStream("content")) { - StreamUtils.copy(is, baos); - } catch (IOException e) { - throw new IllegalStateException("Error while copying data", e); - } - }); - return baos.toByteArray(); - }); + byte[] res = cache.get(id, identifier -> repository.fileContent(id)); try { StreamUtils.copy(res, out); } catch (IOException e) { @@ -89,30 +67,12 @@ public void outputFile(String id, OutputStream out) { public String insertFile(UploadBase64FileModification file) { - Validate.exclusiveBetween(1, MAXIMUM_ALLOWED_SIZE, file.getFile().length); - String digest = DigestUtils.sha256Hex(file.getFile()); - - if(Integer.valueOf(1).equals(repository.isPresent(digest))) { + if (Integer.valueOf(1).equals(repository.isPresent(digest))) { return digest; } - - LobHandler lobHandler = new DefaultLobHandler(); - - - jdbc.getJdbcOperations().execute(repository.uploadTemplate(), - new AbstractLobCreatingPreparedStatementCallback(lobHandler) { - @Override - protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { - ps.setString(1, digest); - ps.setString(2, file.getName()); - ps.setLong(3, file.getFile().length); - lobCreator.setBlobAsBytes(ps, 4, file.getFile()); - ps.setString(5, file.getType()); - ps.setString(6, Json.GSON.toJson(getAttributes(file))); - } - }); + repository.upload(file, digest, getAttributes(file)); return digest; } diff --git a/src/main/java/alfio/manager/GroupManager.java b/src/main/java/alfio/manager/GroupManager.java index f9dd53e997..56a5ca19f5 100644 --- a/src/main/java/alfio/manager/GroupManager.java +++ b/src/main/java/alfio/manager/GroupManager.java @@ -35,8 +35,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -57,18 +55,15 @@ public class GroupManager { private final GroupRepository groupRepository; - private final NamedParameterJdbcTemplate jdbcTemplate; private final TicketRepository ticketRepository; private final AuditingRepository auditingRepository; private final TransactionTemplate requiresNewTransactionTemplate; public GroupManager(GroupRepository groupRepository, - NamedParameterJdbcTemplate jdbcTemplate, TicketRepository ticketRepository, AuditingRepository auditingRepository, PlatformTransactionManager transactionManager) { this.groupRepository = groupRepository; - this.jdbcTemplate = jdbcTemplate; this.ticketRepository = ticketRepository; this.auditingRepository = auditingRepository; this.requiresNewTransactionTemplate = new TransactionTemplate(transactionManager, new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW)); @@ -171,12 +166,7 @@ Result insertMembers(int groupId, List members return new Result.Builder() .checkPrecondition(duplicates::isEmpty, ErrorCode.lazy(() -> ErrorCode.custom("value.duplicate", String.join(", ", duplicates)))) - .build(() -> { - MapSqlParameterSource[] params = members.stream() - .map(i -> new MapSqlParameterSource("groupId", groupId).addValue("value", i.getValue().toLowerCase()).addValue("description", i.getDescription())) - .toArray(MapSqlParameterSource[]::new); - return Arrays.stream(jdbcTemplate.batchUpdate(groupRepository.insertItemTemplate(), params)).sum(); - }); + .build(() -> Arrays.stream(groupRepository.insert(groupId, members)).sum()); } @Transactional @@ -258,8 +248,7 @@ public boolean deactivateMembers(List memberIds, int groupId) { if(memberIds.isEmpty()) { return false; } - MapSqlParameterSource[] params = memberIds.stream().map(i -> toParameterSource(groupId, i)).toArray(MapSqlParameterSource[]::new); - jdbcTemplate.batchUpdate(groupRepository.deactivateGroupMember(), params); + groupRepository.deactivateGroupMember(memberIds, groupId); return true; } @@ -274,12 +263,6 @@ public boolean deactivateGroup(int groupId) { return true; } - private static MapSqlParameterSource toParameterSource(int groupId, Integer itemId) { - return new MapSqlParameterSource("groupId", groupId) - .addValue("memberId", itemId) - .addValue("disabledPlaceholder", UUID.randomUUID().toString()); - } - @RequiredArgsConstructor public static class WhitelistValidator implements Predicate { diff --git a/src/main/java/alfio/manager/UploadedResourceManager.java b/src/main/java/alfio/manager/UploadedResourceManager.java index 25350524db..7aeb095376 100644 --- a/src/main/java/alfio/manager/UploadedResourceManager.java +++ b/src/main/java/alfio/manager/UploadedResourceManager.java @@ -19,26 +19,15 @@ import alfio.model.UploadedResource; import alfio.model.modification.UploadBase64FileModification; import alfio.repository.UploadedResourceRepository; -import alfio.util.Json; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; -import org.springframework.jdbc.support.lob.DefaultLobHandler; -import org.springframework.jdbc.support.lob.LobCreator; -import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StreamUtils; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,12 +41,10 @@ public class UploadedResourceManager { public static final String ATTR_IMG_WIDTH = "width"; public static final String ATTR_IMG_HEIGHT = "height"; - private final NamedParameterJdbcTemplate jdbc; private final UploadedResourceRepository uploadedResourceRepository; @Autowired - public UploadedResourceManager(NamedParameterJdbcTemplate jdbc, UploadedResourceRepository uploadedResourceRepository) { - this.jdbc = jdbc; + public UploadedResourceManager(UploadedResourceRepository uploadedResourceRepository) { this.uploadedResourceRepository = uploadedResourceRepository; } @@ -87,92 +74,39 @@ public UploadedResource get(int organizationId, int eventId, String name) { public void outputResource(String name, OutputStream out) { - SqlParameterSource param = new MapSqlParameterSource("name", name); - jdbc.query(uploadedResourceRepository.fileContentTemplate(name), param, rs -> { - try (InputStream is = rs.getBinaryStream("content")) { - StreamUtils.copy(is, out); - } catch (IOException e) { - throw new IllegalStateException("Error while copying data", e); - } - }); + uploadedResourceRepository.fileContent(name, out); } public void outputResource(int organizationId, String name, OutputStream out) { - SqlParameterSource param = new MapSqlParameterSource("name", name).addValue("organizationId", organizationId); - jdbc.query(uploadedResourceRepository.fileContentTemplate(organizationId, name), param, rs -> { - try (InputStream is = rs.getBinaryStream("content")) { - StreamUtils.copy(is, out); - } catch (IOException e) { - throw new IllegalStateException("Error while copying data", e); - } - }); + uploadedResourceRepository.fileContent(organizationId, name, out); } public void outputResource(int organizationId, int eventId, String name, OutputStream out) { - SqlParameterSource param = new MapSqlParameterSource("name", name).addValue("organizationId", organizationId).addValue("eventId", eventId); - jdbc.query(uploadedResourceRepository.fileContentTemplate(organizationId, eventId, name), param, rs -> { - try (InputStream is = rs.getBinaryStream("content")) { - StreamUtils.copy(is, out); - } catch (IOException e) { - throw new IllegalStateException("Error while copying data", e); - } - }); + uploadedResourceRepository.fileContent(organizationId, eventId, name, out); } public Optional saveResource(UploadBase64FileModification file) { if (hasResource(file.getName())) { uploadedResourceRepository.delete(file.getName()); } - LobHandler lobHandler = new DefaultLobHandler(); - return Optional.ofNullable(jdbc.getJdbcOperations().execute(uploadedResourceRepository.uploadTemplate(file.getName()), - new AbstractLobCreatingPreparedStatementCallback(lobHandler) { - @Override - protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { - setFileValues(ps, lobCreator, file, 1); - } - })); + return Optional.ofNullable(uploadedResourceRepository.upload(null, null, file, getAttributes(file))); } public Optional saveResource(int organizationId, UploadBase64FileModification file) { if (hasResource(organizationId, file.getName())) { uploadedResourceRepository.delete(organizationId, file.getName()); } - LobHandler lobHandler = new DefaultLobHandler(); - return Optional.ofNullable(jdbc.getJdbcOperations().execute(uploadedResourceRepository.uploadTemplate(organizationId, file.getName()), - new AbstractLobCreatingPreparedStatementCallback(lobHandler) { - @Override - protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { - ps.setInt(2, organizationId); - setFileValues(ps, lobCreator, file, 2); - } - })); + + return Optional.ofNullable(uploadedResourceRepository.upload(organizationId, null, file, getAttributes(file))); } public Optional saveResource(int organizationId, int eventId, UploadBase64FileModification file) { if (hasResource(organizationId, eventId, file.getName())) { uploadedResourceRepository.delete(organizationId, eventId, file.getName()); } - LobHandler lobHandler = new DefaultLobHandler(); - return Optional.ofNullable(jdbc.getJdbcOperations().execute(uploadedResourceRepository.uploadTemplate(organizationId, eventId, file.getName()), - new AbstractLobCreatingPreparedStatementCallback(lobHandler) { - @Override - protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { - ps.setInt(2, organizationId); - ps.setInt(3, eventId); - setFileValues(ps, lobCreator, file, 3); - } - })); - - } - - private static void setFileValues(PreparedStatement ps, LobCreator lobCreator, UploadBase64FileModification file, int baseIndex) throws SQLException { - ps.setString(1, file.getName()); - - ps.setLong(baseIndex + 1, file.getFile().length); - lobCreator.setBlobAsBytes(ps, baseIndex + 2, file.getFile()); - ps.setString(baseIndex + 3, file.getType()); - ps.setString(baseIndex + 4, Json.GSON.toJson(getAttributes(file))); + + return Optional.ofNullable(uploadedResourceRepository.upload(organizationId, eventId, file, getAttributes(file))); } public void deleteResource(String name) { diff --git a/src/main/java/alfio/manager/WaitingQueueManager.java b/src/main/java/alfio/manager/WaitingQueueManager.java index cdcec2ed75..b070e2f26e 100644 --- a/src/main/java/alfio/manager/WaitingQueueManager.java +++ b/src/main/java/alfio/manager/WaitingQueueManager.java @@ -46,6 +46,7 @@ import java.time.ZonedDateTime; import java.util.*; import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.Stream; import static alfio.model.system.ConfigurationKeys.*; @@ -190,11 +191,11 @@ private void preReserveTickets(Event event, int ticketsNeeded, int eventId, int .sorted(Comparator.comparing(t -> t.getExpiration(event.getZoneId()))) .map(tc -> Pair.of(determineAvailableSeats(ticketCategoriesStats.get(tc.getId()), eventStatisticView), ticketCategoriesStats.get(tc.getId()))) .collect(new PreReservedTicketDistributor(toBeGenerated)); - MapSqlParameterSource[] candidates = collectedTickets.stream() + List ids = collectedTickets.stream() .flatMap(p -> selectTicketsForPreReservation(eventId, p).stream()) - .map(id -> new MapSqlParameterSource().addValue("id", id)) - .toArray(MapSqlParameterSource[]::new); - jdbc.batchUpdate(ticketRepository.preReserveTicket(), candidates); + .collect(Collectors.toList()); + + ticketRepository.preReserveTicket(ids); } private List selectTicketsForPreReservation(int eventId, Pair p) { diff --git a/src/main/java/alfio/repository/AdminReservationRequestRepository.java b/src/main/java/alfio/repository/AdminReservationRequestRepository.java index 6b253012cf..9c955ea6fb 100644 --- a/src/main/java/alfio/repository/AdminReservationRequestRepository.java +++ b/src/main/java/alfio/repository/AdminReservationRequestRepository.java @@ -18,20 +18,33 @@ import alfio.model.AdminReservationRequest; import alfio.model.AdminReservationRequestStats; +import alfio.model.EventAndOrganizationId; +import alfio.model.modification.AdminReservationModification; +import alfio.util.Json; import ch.digitalfondue.npjt.Bind; import ch.digitalfondue.npjt.Query; import ch.digitalfondue.npjt.QueryRepository; -import ch.digitalfondue.npjt.QueryType; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; @QueryRepository public interface AdminReservationRequestRepository { - @Query(value = "insert into admin_reservation_request(user_id, request_id, event_id, request_type, status, body) values(:userId, :requestId, :eventId, :requestType, :status, :body)", - type = QueryType.TEMPLATE) - String insertRequest(); + default void insertRequest(String requestId, long userId, EventAndOrganizationId event, Stream requestModifications) { + MapSqlParameterSource[] requests = requestModifications.map(res -> new MapSqlParameterSource("userId", userId) + .addValue("requestId", requestId) + .addValue("requestType", AdminReservationRequest.RequestType.IMPORT.name()) + .addValue("status", AdminReservationRequest.Status.PENDING.name()) + .addValue("eventId", event.getId()) + .addValue("body", Json.toJson(res))) + .toArray(MapSqlParameterSource[]::new); + + getNamedParameterJdbcTemplate().batchUpdate("insert into admin_reservation_request(user_id, request_id, event_id, request_type, status, body) values(:userId, :requestId, :eventId, :requestType, :status, :body)", requests); + } @Query("select id from admin_reservation_request where status = 'PENDING' order by request_id limit :limit for update skip locked") List findPendingForUpdate(@Bind("limit") int limit); @@ -39,11 +52,16 @@ public interface AdminReservationRequestRepository { @Query("select * from admin_reservation_request where id = :id") AdminReservationRequest fetchCompleteById(@Bind("id") long id); - @Query(value = "update admin_reservation_request set status = :status, reservation_id = :reservationId, failure_code = :failureCode where id = :id", type = QueryType.TEMPLATE) - String updateStatus(); + //todo, would be better to have more sane parameters, we are leaking the details here + default void updateStatus(List params) { + getNamedParameterJdbcTemplate().batchUpdate("update admin_reservation_request set status = :status, reservation_id = :reservationId, failure_code = :failureCode where id = :id", params.toArray(new MapSqlParameterSource[0])); + } @Query("select * from admin_reservation_request_stats where request_id = :requestId and event_id = :eventId") Optional findStatsByRequestIdAndEventId(@Bind("requestId") String requestId, @Bind("eventId") long eventId); + + NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); + } diff --git a/src/main/java/alfio/repository/FileUploadRepository.java b/src/main/java/alfio/repository/FileUploadRepository.java index a1359af795..5a17abb324 100644 --- a/src/main/java/alfio/repository/FileUploadRepository.java +++ b/src/main/java/alfio/repository/FileUploadRepository.java @@ -17,12 +17,27 @@ package alfio.repository; import alfio.model.FileBlobMetadata; +import alfio.model.modification.UploadBase64FileModification; +import alfio.util.Json; import ch.digitalfondue.npjt.Bind; import ch.digitalfondue.npjt.Query; import ch.digitalfondue.npjt.QueryRepository; -import ch.digitalfondue.npjt.QueryType; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; +import org.springframework.jdbc.support.lob.DefaultLobHandler; +import org.springframework.jdbc.support.lob.LobCreator; +import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.util.StreamUtils; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.PreparedStatement; +import java.sql.SQLException; import java.util.Date; +import java.util.Map; import java.util.Optional; @QueryRepository @@ -37,11 +52,38 @@ public interface FileUploadRepository { @Query("delete from file_blob where creation_time <= :date and id not in (select file_blob_id from event where file_blob_id is not null)") int cleanupUnreferencedBlobFiles(@Bind("date") Date date); - @Query(type = QueryType.TEMPLATE, value = "insert into file_blob (id, name, content_size, content, content_type, attributes) " + - "values(?, ?, ?, ?, ?, ?)") - String uploadTemplate(); + default void upload(UploadBase64FileModification file, String digest, Map attributes) { + LobHandler lobHandler = new DefaultLobHandler(); - @Query(type = QueryType.TEMPLATE, value = "select content from file_blob where id = :id") - String fileContent(@Bind("id") String id); + NamedParameterJdbcTemplate jdbc = getNamedParameterJdbcTemplate(); + + jdbc.getJdbcOperations().execute("insert into file_blob (id, name, content_size, content, content_type, attributes) values(?, ?, ?, ?, ?, ?)", + new AbstractLobCreatingPreparedStatementCallback(lobHandler) { + @Override + protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { + ps.setString(1, digest); + ps.setString(2, file.getName()); + ps.setLong(3, file.getFile().length); + lobCreator.setBlobAsBytes(ps, 4, file.getFile()); + ps.setString(5, file.getType()); + ps.setString(6, Json.GSON.toJson(attributes)); + } + }); + } + + NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); + + default byte[] fileContent(String id) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + SqlParameterSource param = new MapSqlParameterSource("id", id); + getNamedParameterJdbcTemplate().query("select content from file_blob where id = :id", param, rs -> { + try (InputStream is = rs.getBinaryStream("content")) { + StreamUtils.copy(is, baos); + } catch (IOException e) { + throw new IllegalStateException("Error while copying data", e); + } + }); + return baos.toByteArray(); + } } diff --git a/src/main/java/alfio/repository/GroupRepository.java b/src/main/java/alfio/repository/GroupRepository.java index d57bb57f0a..ad42e856d0 100644 --- a/src/main/java/alfio/repository/GroupRepository.java +++ b/src/main/java/alfio/repository/GroupRepository.java @@ -19,10 +19,14 @@ import alfio.model.group.Group; import alfio.model.group.GroupMember; import alfio.model.group.LinkedGroup; +import alfio.model.modification.GroupMemberModification; import ch.digitalfondue.npjt.*; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import java.util.List; import java.util.Optional; +import java.util.UUID; @QueryRepository public interface GroupRepository { @@ -57,9 +61,15 @@ AffectedRowCountAndKey createConfiguration(@Bind("groupId") int groupId @Bind("matchType") LinkedGroup.MatchType matchType, @Bind("maxAllocation") Integer maxAllocation); - @Query(type = QueryType.TEMPLATE, - value = "insert into group_member(a_group_id_fk, value, description) values(:groupId, :value, :description)") - String insertItemTemplate(); + default int[] insert(int groupId, List members) { + MapSqlParameterSource[] params = members.stream() + .map(i -> new MapSqlParameterSource("groupId", groupId).addValue("value", i.getValue().toLowerCase()).addValue("description", i.getDescription())) + .toArray(MapSqlParameterSource[]::new); + + return getNamedParameterJdbcTemplate().batchUpdate("insert into group_member(a_group_id_fk, value, description) values(:groupId, :value, :description)", params); + } + + NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); @Query("select * from group_member_active where a_group_id_fk = :groupId order by value") List getItems(@Bind("groupId") int groupId); @@ -118,8 +128,14 @@ int countExistingWhitelistedTickets(@Bind("itemId") int itemId, @Query("delete from whitelisted_ticket where ticket_id_fk in (:ticketIds)") int deleteExistingWhitelistedTickets(@Bind("ticketIds") List ticketIds); - @Query(type = QueryType.TEMPLATE, value = "update group_member set active = false, value = 'DISABLED-' || :disabledPlaceholder where id = :memberId and a_group_id_fk = :groupId") - String deactivateGroupMember(); + default void deactivateGroupMember(List memberIds, int groupId) { + MapSqlParameterSource[] params = memberIds.stream().map(memberId -> + new MapSqlParameterSource("groupId", groupId) + .addValue("memberId", memberId) + .addValue("disabledPlaceholder", UUID.randomUUID().toString()) + ).toArray(MapSqlParameterSource[]::new); + getNamedParameterJdbcTemplate().batchUpdate("update group_member set active = false, value = 'DISABLED-' || :disabledPlaceholder where id = :memberId and a_group_id_fk = :groupId", params); + } @Query("update a_group set active = false where id = :groupId") diff --git a/src/main/java/alfio/repository/SpecialPriceRepository.java b/src/main/java/alfio/repository/SpecialPriceRepository.java index 1dc6eda689..45fa07c62a 100644 --- a/src/main/java/alfio/repository/SpecialPriceRepository.java +++ b/src/main/java/alfio/repository/SpecialPriceRepository.java @@ -17,14 +17,17 @@ package alfio.repository; import alfio.model.SpecialPrice; +import alfio.model.TicketCategory; import ch.digitalfondue.npjt.Bind; import ch.digitalfondue.npjt.Query; import ch.digitalfondue.npjt.QueryRepository; -import ch.digitalfondue.npjt.QueryType; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import java.time.ZonedDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; @QueryRepository public interface SpecialPriceRepository { @@ -71,9 +74,22 @@ public interface SpecialPriceRepository { @Query("update special_price set code = :code, status = 'FREE', sent_ts = null where id = :id") int updateCode(@Bind("code") String code, @Bind("id") int id); - @Query(type = QueryType.TEMPLATE, value = "insert into special_price (code, price_cts, ticket_category_id, status, sent_ts) " + - "values(:code, :priceInCents, :ticketCategoryId, :status, null)") - String bulkInsert(); + NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); + + default void bulkInsert(TicketCategory ticketCategory, int requiredTokens) { + + MapSqlParameterSource[] params = Stream.generate(MapSqlParameterSource::new) + .limit(requiredTokens) + .peek(ps -> { + ps.addValue("code", UUID.randomUUID().toString()); + ps.addValue("priceInCents", ticketCategory.getSrcPriceCts()); + ps.addValue("ticketCategoryId", ticketCategory.getId()); + ps.addValue("status", SpecialPrice.Status.WAITING.name()); + }).toArray(MapSqlParameterSource[]::new); + + getNamedParameterJdbcTemplate() + .batchUpdate("insert into special_price (code, price_cts, ticket_category_id, status, sent_ts) values(:code, :priceInCents, :ticketCategoryId, :status, null)", params); + } @Query("update special_price set status = 'CANCELLED' where ticket_category_id = :categoryId and status in ('FREE', 'WAITING')") diff --git a/src/main/java/alfio/repository/TicketRepository.java b/src/main/java/alfio/repository/TicketRepository.java index 5d6ad6430a..8a051f6836 100644 --- a/src/main/java/alfio/repository/TicketRepository.java +++ b/src/main/java/alfio/repository/TicketRepository.java @@ -16,10 +16,7 @@ */ package alfio.repository; -import alfio.model.FullTicketInfo; -import alfio.model.Ticket; -import alfio.model.TicketInfo; -import alfio.model.TicketWithReservationAndTransaction; +import alfio.model.*; import ch.digitalfondue.npjt.Bind; import ch.digitalfondue.npjt.Query; import ch.digitalfondue.npjt.QueryRepository; @@ -39,12 +36,19 @@ public interface TicketRepository { String REVERT_TO_FREE = "update ticket set status = 'FREE' where status = 'RELEASED' and event_id = :eventId"; - @Query(type = QueryType.TEMPLATE, value = "insert into ticket (uuid, creation, category_id, event_id, status, original_price_cts, paid_price_cts, src_price_cts)" - + "values(:uuid, :creation, :categoryId, :eventId, :status, 0, 0, :srcPriceCts)") - String bulkTicketInitialization(); + //TODO: refactor, try to move the MapSqlParameterSource inside the default method! + default void bulkTicketInitialization(MapSqlParameterSource[] args) { + getNamedParameterJdbcTemplate().batchUpdate("insert into ticket (uuid, creation, category_id, event_id, status, original_price_cts, paid_price_cts, src_price_cts)" + + "values(:uuid, :creation, :categoryId, :eventId, :status, 0, 0, :srcPriceCts)", args); + } - @Query(type = QueryType.TEMPLATE, value = "update ticket set category_id = :categoryId, src_price_cts = :srcPriceCts where id = :id") - String bulkTicketUpdate(); + default void bulkTicketUpdate(List ids, TicketCategory ticketCategory) { + MapSqlParameterSource[] params = ids.stream().map(id -> new MapSqlParameterSource("id", id) + .addValue("categoryId", ticketCategory.getId()) + .addValue("srcPriceCts", ticketCategory.getSrcPriceCts())) + .toArray(MapSqlParameterSource[]::new); + getNamedParameterJdbcTemplate().batchUpdate("update ticket set category_id = :categoryId, src_price_cts = :srcPriceCts where id = :id", params); + } @Query("select id from ticket where status in (:requiredStatuses) and category_id = :categoryId and event_id = :eventId and tickets_reservation_id is null order by id limit :amount for update") List selectTicketInCategoryForUpdate(@Bind("eventId") int eventId, @Bind("categoryId") int categoryId, @Bind("amount") int amount, @Bind("requiredStatuses") List requiredStatus); @@ -247,8 +251,14 @@ default Set findAllReservationsConfirmedButNotAssignedForUpdate(int even @Query(RELEASE_TICKET_QUERY) int releaseTicket(@Bind("reservationId") String reservationId, @Bind("newUuid") String newUuid, @Bind("eventId") int eventId, @Bind("ticketId") int ticketId); - @Query(value = RELEASE_TICKET_QUERY, type = QueryType.TEMPLATE) - String batchReleaseTickets(); + default int[] batchReleaseTickets(String reservationId, List ticketIds, Event event) { + MapSqlParameterSource[] args = ticketIds.stream().map(id -> new MapSqlParameterSource("ticketId", id) + .addValue("reservationId", reservationId) + .addValue("eventId", event.getId()) + .addValue("newUuid", UUID.randomUUID().toString()) + ).toArray(MapSqlParameterSource[]::new); + return getNamedParameterJdbcTemplate().batchUpdate(RELEASE_TICKET_QUERY, args); + } @Query("update ticket set status = 'RELEASED', uuid = :newUuid, " + RESET_TICKET + " where id = :ticketId and status = 'PENDING' and tickets_reservation_id = :reservationId and event_id = :eventId") int releaseExpiredTicket(@Bind("reservationId") String reservationId, @Bind("eventId") int eventId, @Bind("ticketId") int ticketId, @Bind("newUuid") String newUuid); @@ -289,8 +299,12 @@ default void resetTickets(List ticketIds) { @Query("select count(*) from ticket where status = 'PRE_RESERVED'") Integer countPreReservedTickets(@Bind("eventId") int eventId); - @Query(type = QueryType.TEMPLATE, value = "update ticket set status = 'PRE_RESERVED' where id = :id") - String preReserveTicket(); + default void preReserveTicket(List ids) { + MapSqlParameterSource[] params = ids.stream() + .map(id -> new MapSqlParameterSource().addValue("id", id)) + .toArray(MapSqlParameterSource[]::new); + getNamedParameterJdbcTemplate().batchUpdate("update ticket set status = 'PRE_RESERVED' where id = :id", params); + } @Query("select * from ticket where status = 'FREE' and event_id = :eventId") List findFreeByEventId(@Bind("eventId") int eventId); diff --git a/src/main/java/alfio/repository/UploadedResourceRepository.java b/src/main/java/alfio/repository/UploadedResourceRepository.java index 64289c1c01..259798c474 100644 --- a/src/main/java/alfio/repository/UploadedResourceRepository.java +++ b/src/main/java/alfio/repository/UploadedResourceRepository.java @@ -17,9 +17,27 @@ package alfio.repository; import alfio.model.UploadedResource; +import alfio.model.modification.UploadBase64FileModification; +import alfio.util.Json; import ch.digitalfondue.npjt.*; - +import org.springframework.jdbc.core.RowCallbackHandler; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback; +import org.springframework.jdbc.support.lob.DefaultLobHandler; +import org.springframework.jdbc.support.lob.LobCreator; +import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.util.StreamUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.PreparedStatement; +import java.sql.SQLException; import java.util.List; +import java.util.Map; +import java.util.function.Function; @QueryRepository public interface UploadedResourceRepository { @@ -68,29 +86,60 @@ public interface UploadedResourceRepository { int delete(@Bind("organizationId") int organizationId, @Bind("eventId") int eventId, @Bind("name") String name); - - @Query(type = QueryType.TEMPLATE, value = "select content from resource_global where name = :name") - String fileContentTemplate(String name); - - @Query(type = QueryType.TEMPLATE, value = "select content from resource_organizer where name = :name and organization_id_fk = :organizationId") - String fileContentTemplate(int organizationId, String name); - - @Query(type = QueryType.TEMPLATE, value = "select content from resource_event where name = :name and organization_id_fk = :organizationId and event_id_fk = :eventId") - String fileContentTemplate(int organizationId, int eventId, String name); - - - - - @Query(type = QueryType.TEMPLATE, value = "insert into resource_global (name, content_size, content, content_type, attributes) " + - "values(?, ?, ?, ?, ?)") - String uploadTemplate(String name); - - @Query(type = QueryType.TEMPLATE, value = "insert into resource_organizer (name, organization_id_fk, content_size, content, content_type, attributes) " + - "values(?, ?, ?, ?, ?, ?)") - String uploadTemplate(int organizationId, String name); - - @Query(type = QueryType.TEMPLATE, value = "insert into resource_event (name, organization_id_fk, event_id_fk, content_size, content, content_type, attributes) " + - "values(?, ?, ?, ?, ?, ?, ?)") - String uploadTemplate(int organizationId, int eventId, String name); + Function OUTPUT_CONTENT = (out) -> rs -> { + try (InputStream is = rs.getBinaryStream("content")) { + StreamUtils.copy(is, out); + } catch (IOException e) { + throw new IllegalStateException("Error while copying data", e); + } + }; + + default void fileContent(String name, OutputStream out) { + SqlParameterSource param = new MapSqlParameterSource("name", name); + getNamedParameterJdbcTemplate().query("select content from resource_global where name = :name", param, OUTPUT_CONTENT.apply(out)); + } + + default void fileContent(int organizationId, String name, OutputStream out) { + SqlParameterSource param = new MapSqlParameterSource("name", name).addValue("organizationId", organizationId); + getNamedParameterJdbcTemplate().query("select content from resource_organizer where name = :name and organization_id_fk = :organizationId", param, OUTPUT_CONTENT.apply(out)); + } + + default void fileContent(int organizationId, int eventId, String name, OutputStream out) { + SqlParameterSource param = new MapSqlParameterSource("name", name).addValue("organizationId", organizationId).addValue("eventId", eventId); + getNamedParameterJdbcTemplate().query("select content from resource_event where name = :name and organization_id_fk = :organizationId and event_id_fk = :eventId", param, OUTPUT_CONTENT.apply(out)); + } + + NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); + + default int upload(Integer organizationId, Integer eventId, UploadBase64FileModification file, Map attributes) { + + LobHandler lobHandler = new DefaultLobHandler(); + + String query = "insert into resource_global (name, content_size, content, content_type, attributes) values(?, ?, ?, ?, ?)"; + if (organizationId != null && eventId != null) { + query = "insert into resource_event (name, content_size, content, content_type, attributes, organization_id_fk, event_id_fk) values(?, ?, ?, ?, ?, ?, ?)"; + } else if(organizationId != null) { + query = "insert into resource_organizer (name, content_size, content, content_type, attributes, organization_id_fk) values(?, ?, ?, ?, ?, ?)"; + } + + return getNamedParameterJdbcTemplate().getJdbcOperations().execute(query, + new AbstractLobCreatingPreparedStatementCallback(lobHandler) { + @Override + protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException { + ps.setString(1, file.getName()); + ps.setLong(2, file.getFile().length); + lobCreator.setBlobAsBytes(ps, 3, file.getFile()); + ps.setString(4, file.getType()); + ps.setString(5, Json.GSON.toJson(attributes)); + if (organizationId != null) { + ps.setInt(6, organizationId); + } + if (eventId != null) { + ps.setInt(7, eventId); + } + } + } + ); + } } diff --git a/src/test/java/alfio/manager/EventManagerCategoriesTest.java b/src/test/java/alfio/manager/EventManagerCategoriesTest.java index 015ec0cd4d..b76c3db6c7 100644 --- a/src/test/java/alfio/manager/EventManagerCategoriesTest.java +++ b/src/test/java/alfio/manager/EventManagerCategoriesTest.java @@ -56,7 +56,7 @@ void init() { EventRepository eventRepository = mock(EventRepository.class); event = mock(Event.class); when(event.getId()).thenReturn(eventId); - eventManager = new EventManager(null, eventRepository, null, ticketCategoryRepository, ticketCategoryDescriptionRepository, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + eventManager = new EventManager(null, eventRepository, null, ticketCategoryRepository, ticketCategoryDescriptionRepository, null, null, null, null, null, null, null, null, null, null, null, null, null, null); when(eventRepository.countExistingTickets(0)).thenReturn(availableSeats); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); } diff --git a/src/test/java/alfio/manager/EventManagerHandleTicketModificationTest.java b/src/test/java/alfio/manager/EventManagerHandleTicketModificationTest.java index 4d24579e51..c519843fdf 100644 --- a/src/test/java/alfio/manager/EventManagerHandleTicketModificationTest.java +++ b/src/test/java/alfio/manager/EventManagerHandleTicketModificationTest.java @@ -23,21 +23,16 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.jdbc.core.namedparam.SqlParameterSource; import java.time.ZoneId; import java.util.Arrays; import java.util.List; import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @@ -47,7 +42,6 @@ public class EventManagerHandleTicketModificationTest { private TicketCategory original; private TicketCategory updated; private TicketRepository ticketRepository; - private NamedParameterJdbcTemplate jdbc; private EventManager eventManager; private final int eventId = 10; private int originalCategoryId = 20; @@ -60,9 +54,9 @@ void init() { original = mock(TicketCategory.class); updated = mock(TicketCategory.class); ticketRepository = mock(TicketRepository.class); - jdbc = mock(NamedParameterJdbcTemplate.class); + when(event.getId()).thenReturn(eventId); - eventManager = new EventManager(null, null, null, null, null, ticketRepository, null, null, jdbc, null, null, null, null, null, null, null, null, null, null, null); + eventManager = new EventManager(null, null, null, null, null, ticketRepository, null, null, null, null, null, null, null, null, null, null, null, null, null); when(original.getId()).thenReturn(originalCategoryId); when(updated.getId()).thenReturn(updatedCategoryId); when(original.getSrcPriceCts()).thenReturn(1000); @@ -71,7 +65,6 @@ void init() { when(updated.getMaxTickets()).thenReturn(11); when(original.isBounded()).thenReturn(true); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); - when(ticketRepository.bulkTicketUpdate()).thenReturn("bulk"); } @DisplayName("throw exception if there are tickets already sold") @@ -96,7 +89,7 @@ void invalidateExceedingTickets() { void doNothingIfZero() { eventManager.handleTicketNumberModification(event, original, updated, 0, false); verify(ticketRepository, never()).invalidateTickets(anyList()); - verify(jdbc, never()).batchUpdate(anyString(), any(SqlParameterSource[].class)); + verify(ticketRepository, never()).bulkTicketUpdate(any(), any()); } @Test @@ -105,9 +98,7 @@ void insertTicketIfDifference1() { when(ticketRepository.selectNotAllocatedTicketsForUpdate(eq(eventId), eq(1), eq(Arrays.asList(Ticket.TicketStatus.FREE.name(), Ticket.TicketStatus.RELEASED.name())))).thenReturn(singletonList(1)); eventManager.handleTicketNumberModification(event, original, updated, 1, false); verify(ticketRepository, never()).invalidateTickets(anyList()); - ArgumentCaptor captor = ArgumentCaptor.forClass(SqlParameterSource[].class); - verify(jdbc, times(1)).batchUpdate(anyString(), captor.capture()); - assertEquals(1, captor.getValue().length); + verify(ticketRepository, times(1)).bulkTicketUpdate(any(), any()); } @Test diff --git a/src/test/java/alfio/manager/EventManagerHandleTokenModificationTest.java b/src/test/java/alfio/manager/EventManagerHandleTokenModificationTest.java index a495fec1cc..1f8906983a 100644 --- a/src/test/java/alfio/manager/EventManagerHandleTokenModificationTest.java +++ b/src/test/java/alfio/manager/EventManagerHandleTokenModificationTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import java.time.ZoneId; @@ -32,7 +31,6 @@ import java.util.List; import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; @@ -41,7 +39,6 @@ class EventManagerHandleTokenModificationTest { private TicketCategory original; private TicketCategory updated; - private NamedParameterJdbcTemplate jdbc; private SpecialPriceRepository specialPriceRepository; private EventManager eventManager; private final int eventId = 10; @@ -51,13 +48,12 @@ class EventManagerHandleTokenModificationTest { void init() { original = mock(TicketCategory.class); updated = mock(TicketCategory.class); - jdbc = mock(NamedParameterJdbcTemplate.class); specialPriceRepository = mock(SpecialPriceRepository.class); Event event = mock(Event.class); TicketRepository ticketRepository = mock(TicketRepository.class); when(event.getId()).thenReturn(eventId); eventManager = new EventManager(null, null, null, null, - null, ticketRepository, specialPriceRepository, null, jdbc, null, null, null, null, null, null, null, null, null, null, null); + null, ticketRepository, specialPriceRepository, null, null, null, null, null, null, null, null, null, null, null, null); when(original.getId()).thenReturn(20); when(updated.getId()).thenReturn(30); when(original.getSrcPriceCts()).thenReturn(1000); @@ -65,7 +61,6 @@ void init() { when(original.getMaxTickets()).thenReturn(10); when(updated.getMaxTickets()).thenReturn(11); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); - when(specialPriceRepository.bulkInsert()).thenReturn("aaaa"); } @@ -75,19 +70,17 @@ void doNothingIfCategoriesAreNotAccessRestricted() { when(original.isAccessRestricted()).thenReturn(false); when(updated.isAccessRestricted()).thenReturn(false); eventManager.handleTokenModification(original, updated, 50); - verify(jdbc, never()).batchUpdate(anyString(), any(SqlParameterSource[].class)); + verify(specialPriceRepository, never()).bulkInsert(any(TicketCategory.class), anyInt()); } @Test @DisplayName("handle the activation of access restriction") void handleRestrictionActivation() { - ArgumentCaptor captor = ArgumentCaptor.forClass(SqlParameterSource[].class); when(original.isAccessRestricted()).thenReturn(false); when(updated.isAccessRestricted()).thenReturn(true); when(updated.getMaxTickets()).thenReturn(50); eventManager.handleTokenModification(original, updated, 50); - verify(jdbc, times(1)).batchUpdate(anyString(), captor.capture()); - assertEquals(50, captor.getValue().length); + verify(specialPriceRepository, times(1)).bulkInsert(updated, updated.getMaxTickets()); } @Test @@ -107,8 +100,7 @@ void handleTicketAddition() { when(original.isAccessRestricted()).thenReturn(true); when(updated.isAccessRestricted()).thenReturn(true); eventManager.handleTokenModification(original, updated, 50); - verify(jdbc, times(1)).batchUpdate(anyString(), captor.capture()); - assertEquals(50, captor.getValue().length); + verify(specialPriceRepository, times(1)).bulkInsert(updated, 50); } @Test diff --git a/src/test/java/alfio/manager/EventManagerUnbindTicketsTest.java b/src/test/java/alfio/manager/EventManagerUnbindTicketsTest.java index fabb3c9fc6..87528d3b60 100644 --- a/src/test/java/alfio/manager/EventManagerUnbindTicketsTest.java +++ b/src/test/java/alfio/manager/EventManagerUnbindTicketsTest.java @@ -85,7 +85,7 @@ void setUp() { eventDescriptionRepository, ticketCategoryRepository, ticketCategoryDescriptionRepository, ticketRepository, specialPriceRepository, null, null, null, null, null, null, - null, null, null, organizationRepository, + null, null, organizationRepository, null, null, null); } diff --git a/src/test/java/alfio/manager/WaitingQueueManagerTest.java b/src/test/java/alfio/manager/WaitingQueueManagerTest.java index 69a58dfdfd..821a2d8891 100644 --- a/src/test/java/alfio/manager/WaitingQueueManagerTest.java +++ b/src/test/java/alfio/manager/WaitingQueueManagerTest.java @@ -157,7 +157,7 @@ void processPreReservations() { verify(ticketRepository).countWaiting(eq(eventId)); verify(ticketRepository, never()).revertToFree(eq(eventId)); verify(ticketRepository).countPreReservedTickets(eq(eventId)); - verify(ticketRepository).preReserveTicket(); + verify(ticketRepository).preReserveTicket(anyList()); verify(ticketRepository).selectWaitingTicketsForUpdate(eq(eventId), anyString(), anyInt()); } } \ No newline at end of file