package de.rwth.idsg.bikeman.repository;
import de.rwth.idsg.bikeman.domain.*;
import de.rwth.idsg.bikeman.domain.CardAccount_;
import de.rwth.idsg.bikeman.domain.Customer_;
import de.rwth.idsg.bikeman.domain.MajorCustomer_;
import de.rwth.idsg.bikeman.domain.Pedelec_;
import de.rwth.idsg.bikeman.domain.StationSlot_;
import de.rwth.idsg.bikeman.domain.Station_;
import de.rwth.idsg.bikeman.domain.Transaction_;
import de.rwth.idsg.bikeman.domain.User_;
import de.rwth.idsg.bikeman.web.rest.dto.view.ViewTransactionDTO;
import de.rwth.idsg.bikeman.web.rest.exception.DatabaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import java.util.Collections;
import java.util.List;
/**
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
* @since 27.05.2014
*/
@Repository
@Slf4j
public class TransactionRepositoryImpl implements TransactionRepository {
private enum FindType {
ALL, CLOSED, OPEN,
BY_PEDELEC_ID, BY_LOGIN
}
@PersistenceContext private EntityManager em;
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findAllCustomerTransactions() throws DatabaseException {
return internalFind(FindType.ALL, CustomerType.CUSTOMER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findClosedCustomerTransactions() throws DatabaseException {
return internalFind(FindType.CLOSED, CustomerType.CUSTOMER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findOpenCustomerTransactions() throws DatabaseException {
return internalFind(FindType.OPEN, CustomerType.CUSTOMER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findAllMajorCustomerTransactions() throws DatabaseException {
return internalFind(FindType.ALL, CustomerType.MAJOR_CUSTOMER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findFleetManagerTransactions() throws DatabaseException {
return internalFind(FindType.ALL, CustomerType.FLEET_MANAGER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findClosedMajorCustomerTransactions() throws DatabaseException {
return internalFind(FindType.CLOSED, CustomerType.MAJOR_CUSTOMER);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findOpenMajorCustomerTransactions() throws DatabaseException {
return internalFind(FindType.OPEN, CustomerType.MAJOR_CUSTOMER);
}
@Override
public List<ViewTransactionDTO> findTransactionsByPedelecId(Long pedelecId, Integer resultSize) throws DatabaseException {
CriteriaBuilder builder = em.getCriteriaBuilder();
List<ViewTransactionDTO> returnList =
setResultSizeAndGet(
em.createQuery(getTransactionQuery(builder, FindType.BY_PEDELEC_ID, CustomerType.MAJOR_CUSTOMER, pedelecId, null)),
resultSize
);
returnList.addAll(
setResultSizeAndGet(
em.createQuery(getTransactionQuery(builder, FindType.BY_PEDELEC_ID, CustomerType.CUSTOMER, pedelecId, null)),
resultSize
));
returnList.addAll(
setResultSizeAndGet(
em.createQuery(getTransactionQuery(builder, FindType.BY_PEDELEC_ID, CustomerType.FLEET_MANAGER, pedelecId, null)),
resultSize
));
// sort by date DESC
returnList.sort((t1, t2) -> t2.getStartDateTime().compareTo(t1.getStartDateTime()));
return returnList;
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findCustomerTransactionsByLogin(String login, Integer resultSize)
throws DatabaseException {
CriteriaBuilder builder = em.getCriteriaBuilder();
return setResultSizeAndGet(
em.createQuery(getTransactionQuery(builder, FindType.BY_LOGIN, CustomerType.CUSTOMER, null, login)),
resultSize
);
}
@Override
@Transactional(readOnly = true)
public List<ViewTransactionDTO> findMajorCustomerTransactionsByLogin(String login, Integer resultSize)
throws DatabaseException {
CriteriaBuilder builder = em.getCriteriaBuilder();
return setResultSizeAndGet(
em.createQuery(getTransactionQuery(builder, FindType.BY_LOGIN, CustomerType.MAJOR_CUSTOMER, null, login)),
resultSize
);
}
/**
* Delete booking and transaction and set all related DB entries to default.
*
* As if it did not exist.
*
* ONLY FOR DEBUGGING!
*/
@Override
@Transactional(readOnly = false)
public void kill(long transactionId) {
Transaction transaction = em.find(Transaction.class, transactionId);
em.createQuery("DELETE FROM Booking b WHERE b.transaction = :transaction")
.setParameter("transaction", transaction)
.executeUpdate();
em.createQuery("UPDATE CardAccount ca SET ca.inTransaction = false WHERE ca = :cardAccount")
.setParameter("cardAccount", transaction.getCardAccount())
.executeUpdate();
em.createQuery("UPDATE Pedelec p SET p.inTransaction = false WHERE p = :pedelec")
.setParameter("pedelec", transaction.getPedelec())
.executeUpdate();
// Register the pedelec back at the starting station slot
em.createQuery("UPDATE StationSlot ss SET ss.isOccupied = true, ss.pedelec = :pedelec WHERE ss = :stationSlot")
.setParameter("pedelec", transaction.getPedelec())
.setParameter("stationSlot", transaction.getFromSlot())
.executeUpdate();
em.remove(transaction);
}
// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------
private List<ViewTransactionDTO> internalFind(FindType findType, CustomerType customerType) throws DatabaseException {
CriteriaBuilder builder = em.getCriteriaBuilder();
try {
return em.createQuery(getTransactionQuery(builder, findType, customerType, null, null)).getResultList();
} catch (Exception e) {
throw new DatabaseException("Failed during database operation.", e);
}
}
private List<ViewTransactionDTO> setResultSizeAndGet(TypedQuery<ViewTransactionDTO> tqc, Integer resultSize) {
if (resultSize != null) {
tqc.setMaxResults(resultSize);
}
try {
return tqc.getResultList();
} catch (Exception e) {
throw new DatabaseException("Failed during database operation.", e);
}
}
@SuppressWarnings("unchecked")
private CriteriaQuery<ViewTransactionDTO> getTransactionQuery(CriteriaBuilder builder,
FindType findType, CustomerType customerType,
Long pedelecId, String login) {
CriteriaQuery<ViewTransactionDTO> criteria = builder.createQuery(ViewTransactionDTO.class);
Root<Transaction> transaction = criteria.from(Transaction.class);
Join<Transaction, Pedelec> pedelec = transaction.join(Transaction_.pedelec, JoinType.LEFT);
Join<Transaction, StationSlot> fromStationSlot = transaction.join(Transaction_.fromSlot, JoinType.LEFT);
Join<StationSlot, Station> fromStation = fromStationSlot.join(StationSlot_.station, JoinType.LEFT);
Join<Transaction, CardAccount> cardAccount = transaction.join(Transaction_.cardAccount, JoinType.LEFT);
Join user = cardAccount.join(CardAccount_.user, JoinType.LEFT);
Join<Transaction, StationSlot> toStationSlot = transaction.join(Transaction_.toSlot, JoinType.LEFT);
Join<StationSlot, Station> toStation = toStationSlot.join(StationSlot_.station, JoinType.LEFT);
// -------------------------------------------------------------------------
// Customer type decisions
// -------------------------------------------------------------------------
Selection<ViewTransactionDTO> selection = null;
switch (customerType) {
case MAJOR_CUSTOMER:
selection = builder.construct(
ViewTransactionDTO.class,
transaction.get(Transaction_.transactionId),
transaction.get(Transaction_.startDateTime),
transaction.get(Transaction_.endDateTime),
fromStation.get(Station_.stationId),
fromStation.get(Station_.name),
fromStationSlot.get(StationSlot_.stationSlotPosition),
toStation.get(Station_.stationId),
toStation.get(Station_.name),
toStationSlot.get(StationSlot_.stationSlotPosition),
cardAccount.get(CardAccount_.cardId),
user.get(MajorCustomer_.name),
pedelec.get(Pedelec_.pedelecId),
pedelec.get(Pedelec_.manufacturerId)
);
break;
case FLEET_MANAGER:
selection = builder.construct(
ViewTransactionDTO.class,
user.get(Manager_.login),
transaction.get(Transaction_.transactionId),
transaction.get(Transaction_.startDateTime),
transaction.get(Transaction_.endDateTime),
fromStation.get(Station_.stationId),
fromStation.get(Station_.name),
fromStationSlot.get(StationSlot_.stationSlotPosition),
toStation.get(Station_.stationId),
toStation.get(Station_.name),
toStationSlot.get(StationSlot_.stationSlotPosition),
cardAccount.get(CardAccount_.cardId),
pedelec.get(Pedelec_.pedelecId),
pedelec.get(Pedelec_.manufacturerId)
);
break;
case CUSTOMER:
selection = builder.construct(
ViewTransactionDTO.class,
transaction.get(Transaction_.transactionId),
transaction.get(Transaction_.startDateTime),
transaction.get(Transaction_.endDateTime),
fromStation.get(Station_.stationId),
fromStation.get(Station_.name),
fromStationSlot.get(StationSlot_.stationSlotPosition),
toStation.get(Station_.stationId),
toStation.get(Station_.name),
toStationSlot.get(StationSlot_.stationSlotPosition),
cardAccount.get(CardAccount_.cardId),
user.get(Customer_.customerId),
user.get(Customer_.firstname),
user.get(Customer_.lastname),
pedelec.get(Pedelec_.pedelecId),
pedelec.get(Pedelec_.manufacturerId)
);
break;
}
criteria.select(selection)
.orderBy(builder.desc(transaction.get(Transaction_.endDateTime)));
// -------------------------------------------------------------------------
// Find type decisions
// -------------------------------------------------------------------------
Predicate findPredicate = builder.and();
switch (findType) {
case ALL:
break;
case CLOSED:
findPredicate = builder.and(
builder.isNotNull(transaction.get(Transaction_.toSlot)),
builder.isNotNull(transaction.get(Transaction_.endDateTime))
);
break;
case OPEN:
findPredicate = builder.and(
builder.isNull(transaction.get(Transaction_.toSlot)),
builder.isNull(transaction.get(Transaction_.endDateTime))
);
break;
case BY_PEDELEC_ID:
findPredicate = builder.equal(pedelec.get(Pedelec_.pedelecId), pedelecId);
break;
case BY_LOGIN:
findPredicate = builder.equal(user.get(User_.login), login);
break;
}
return criteria.where(
builder.and(
builder.equal(cardAccount.get(CardAccount_.ownerType), customerType),
findPredicate
)
);
}
}