package de.rwth.idsg.bikeman.app.repository;
import de.rwth.idsg.bikeman.app.dto.StationSlotsDTO;
import de.rwth.idsg.bikeman.app.dto.ViewStationDTO;
import de.rwth.idsg.bikeman.app.exception.AppErrorCode;
import de.rwth.idsg.bikeman.app.exception.AppException;
import de.rwth.idsg.bikeman.domain.Pedelec;
import de.rwth.idsg.bikeman.domain.PedelecChargingStatus;
import de.rwth.idsg.bikeman.domain.PedelecChargingStatus_;
import de.rwth.idsg.bikeman.domain.Pedelec_;
import de.rwth.idsg.bikeman.domain.Station;
import de.rwth.idsg.bikeman.domain.StationSlot;
import de.rwth.idsg.bikeman.domain.StationSlot_;
import de.rwth.idsg.bikeman.domain.Station_;
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.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import java.util.List;
@Repository
@Slf4j
public class AppStationRepositoryImpl implements AppStationRepository {
@PersistenceContext
private EntityManager em;
@Override
@Transactional(readOnly = true)
public List<ViewStationDTO> findAll() throws AppException {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ViewStationDTO> criteria = this.getStationQuery(builder, null);
try {
return em.createQuery(criteria).getResultList();
} catch (Exception e) {
throw new DatabaseException("Failed during database operation.", e);
}
}
@Override
@Transactional(readOnly = true)
public ViewStationDTO findOne(long stationId) throws AppException {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ViewStationDTO> criteria = this.getStationQuery(builder, stationId);
try {
return em.createQuery(criteria).getSingleResult();
} catch (NoResultException e) {
throw new AppException("Failed to find station with stationId " + stationId, e, AppErrorCode.CONSTRAINT_FAILED);
} catch (Exception e) {
throw new AppException("Failed during database operation", e, AppErrorCode.DATABASE_OPERATION_FAILED);
}
}
@Override
@Transactional(readOnly = true)
public List<StationSlotsDTO> findOneWithSlots(long stationId) throws AppException {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<StationSlotsDTO> criteria = builder.createQuery(StationSlotsDTO.class);
Root<StationSlot> stationSlot = criteria.from(StationSlot.class);
Join<StationSlot, Pedelec> pedelec = stationSlot.join(StationSlot_.pedelec, JoinType.LEFT);
Join<Pedelec, PedelecChargingStatus> chargingStatus = pedelec.join(Pedelec_.chargingStatus, JoinType.LEFT);
criteria.select(
builder.construct(
StationSlotsDTO.class,
stationSlot.get(StationSlot_.stationSlotId),
stationSlot.get(StationSlot_.stationSlotPosition),
stationSlot.get(StationSlot_.state),
stationSlot.get(StationSlot_.isOccupied),
builder.quot(chargingStatus.get(PedelecChargingStatus_.batteryStateOfCharge), 100)
)
).where(
builder.equal(stationSlot.get(StationSlot_.station).get(Station_.stationId), stationId)
).orderBy(
builder.asc(stationSlot.get(StationSlot_.stationSlotPosition))
);
try {
List<StationSlotsDTO> slots = em.createQuery(criteria).getResultList();
if (slots.isEmpty()) {
throw new NoResultException();
}
return slots;
} catch (NoResultException e) {
throw new AppException("Failed to find station with stationId " + stationId, e, AppErrorCode.CONSTRAINT_FAILED);
} catch (Exception e) {
throw new AppException("Failed during database operation", e, AppErrorCode.DATABASE_OPERATION_FAILED);
}
}
private CriteriaQuery<ViewStationDTO> getStationQuery(CriteriaBuilder builder, Long stationId) {
CriteriaQuery<ViewStationDTO> criteria = builder.createQuery(ViewStationDTO.class);
Root<Station> station = criteria.from(Station.class);
Join<Station, StationSlot> stationSlot = station.join(Station_.stationSlots, JoinType.LEFT);
Path<Boolean> occ = stationSlot.get(StationSlot_.isOccupied);
criteria.select(
builder.construct(
ViewStationDTO.class,
station.get(Station_.stationId),
station.get(Station_.name),
station.get(Station_.locationLatitude),
station.get(Station_.locationLongitude),
station.get(Station_.state),
station.get(Station_.note),
builder.sum(builder.<Integer>selectCase()
.when(builder.isFalse(occ), 1)
.otherwise(0)
),
builder.count(occ)
)
);
if (stationId != null) {
criteria.where(builder.equal(station.get(Station_.stationId), stationId));
}
criteria.groupBy(station.get(Station_.stationId));
return criteria;
}
}