// Copyright © 2015 HSL <https://www.hsl.fi> // This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses. package fi.hsl.parkandride.back.prediction; import com.querydsl.core.Tuple; import com.querydsl.sql.SQLExpressions; import com.querydsl.sql.postgresql.PostgreSQLQueryFactory; import com.querydsl.core.types.MappingProjection; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.SimpleExpression; import fi.hsl.parkandride.back.sql.QPredictor; import fi.hsl.parkandride.core.back.PredictorRepository; import fi.hsl.parkandride.core.domain.UtilizationKey; import fi.hsl.parkandride.core.domain.prediction.PredictorState; import fi.hsl.parkandride.core.service.TransactionalRead; import fi.hsl.parkandride.core.service.TransactionalWrite; import fi.hsl.parkandride.core.service.ValidationService; import java.util.List; import java.util.Objects; public class PredictorDao implements PredictorRepository { public static final String PREDICTOR_ID_SEQ = "facility_id_seq"; private static final SimpleExpression<Long> nextPredictorId = SQLExpressions.nextval(PREDICTOR_ID_SEQ); private static final QPredictor qPredictor = QPredictor.predictor; private static final MappingProjection<PredictorState> predictorMapping = new MappingProjection<PredictorState>(PredictorState.class, qPredictor.all()) { @Override protected PredictorState map(Tuple row) { UtilizationKey utilizationKey = new UtilizationKey(row.get(qPredictor.facilityId), row.get(qPredictor.capacityType), row.get(qPredictor.usage)); PredictorState state = new PredictorState(row.get(qPredictor.id), row.get(qPredictor.type), utilizationKey); state.latestUtilization = row.get(qPredictor.latestUtilization); state.moreUtilizations = row.get(qPredictor.moreUtilizations); state.internalState = row.get(qPredictor.internalState); return state; } }; private static Predicate[] utilizationKeyEquals(UtilizationKey utilizationKey) { return new Predicate[]{ qPredictor.facilityId.eq(utilizationKey.facilityId), qPredictor.capacityType.eq(utilizationKey.capacityType), qPredictor.usage.eq(utilizationKey.usage) }; } private final PostgreSQLQueryFactory queryFactory; private final ValidationService validationService; public PredictorDao(PostgreSQLQueryFactory queryFactory, ValidationService validationService) { this.queryFactory = queryFactory; this.validationService = validationService; } @TransactionalWrite @Override public Long enablePredictor(String predictorType, UtilizationKey utilizationKey) { validationService.validate(utilizationKey); Long existing = queryFactory.from(qPredictor) .where(qPredictor.type.eq(predictorType)) .where(utilizationKeyEquals(utilizationKey)) .select(qPredictor.id) .fetchOne(); if (existing != null) { return existing; } queryFactory.insert(qPredictor) .set(qPredictor.id, queryFactory.query().select(nextPredictorId).fetchOne()) .set(qPredictor.type, predictorType) .set(qPredictor.facilityId, utilizationKey.facilityId) .set(qPredictor.capacityType, utilizationKey.capacityType) .set(qPredictor.usage, utilizationKey.usage) .execute(); return enablePredictor(predictorType, utilizationKey); } @TransactionalWrite @Override public void save(PredictorState state) { validationService.validate(state); queryFactory.update(qPredictor) .set(qPredictor.latestUtilization, state.latestUtilization) .set(qPredictor.moreUtilizations, state.moreUtilizations) .set(qPredictor.internalState, state.internalState) .where(qPredictor.id.eq(state.predictorId)) .execute(); } @TransactionalRead @Override public PredictorState getById(Long predictorId) { return Objects.requireNonNull( queryFactory.from(qPredictor) .where(qPredictor.id.eq(predictorId)) .select(predictorMapping).fetchOne(), "No predictors with id " + predictorId); } @TransactionalRead @Override public PredictorState getForUpdate(Long predictorId) { return Objects.requireNonNull( queryFactory.from(qPredictor) .where(qPredictor.id.eq(predictorId)) .forUpdate() .select(predictorMapping).fetchOne(), "No predictors with id " + predictorId); } @TransactionalRead @Override public List<PredictorState> findAllPredictors() { return queryFactory.from(qPredictor) .select(predictorMapping).fetch(); } @TransactionalRead @Override public List<Long> findPredictorsNeedingUpdate() { return queryFactory.from(qPredictor) .where(qPredictor.moreUtilizations.eq(true)) .select(qPredictor.id).fetch(); } @TransactionalWrite @Override public void markPredictorsNeedAnUpdate(UtilizationKey utilizationKey) { validationService.validate(utilizationKey); queryFactory.update(qPredictor) .set(qPredictor.moreUtilizations, true) .where(utilizationKeyEquals(utilizationKey)) .execute(); } }