// 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; import com.querydsl.core.Tuple; import com.querydsl.core.types.MappingProjection; import com.querydsl.core.types.dsl.ComparableExpression; import com.querydsl.core.types.dsl.SimpleExpression; import com.querydsl.sql.SQLExpressions; import com.querydsl.sql.dml.SQLInsertClause; import com.querydsl.sql.dml.SQLUpdateClause; import com.querydsl.sql.postgresql.PostgreSQLQuery; import com.querydsl.sql.postgresql.PostgreSQLQueryFactory; import fi.hsl.parkandride.back.sql.QOperator; import fi.hsl.parkandride.core.back.OperatorRepository; import fi.hsl.parkandride.core.domain.*; import fi.hsl.parkandride.core.service.TransactionalRead; import fi.hsl.parkandride.core.service.TransactionalWrite; import fi.hsl.parkandride.core.service.ValidationException; import static com.google.common.base.MoreObjects.firstNonNull; import static fi.hsl.parkandride.core.domain.Sort.Dir.ASC; import static fi.hsl.parkandride.core.domain.Sort.Dir.DESC; public class OperatorDao implements OperatorRepository { public static final String OPERATOR_ID_SEQ = "operator_id_seq"; private static final SimpleExpression<Long> nextOperatorId = SQLExpressions.nextval(OPERATOR_ID_SEQ); private static final Sort DEFAULT_SORT = new Sort("name.fi", ASC); private static QOperator qOperator = QOperator.operator; private static MultilingualStringMapping nameMapping = new MultilingualStringMapping(qOperator.nameFi, qOperator.nameSv, qOperator.nameEn); private static MappingProjection<Operator> operatorMapping = new MappingProjection<Operator>(Operator.class, qOperator.all()) { @Override protected Operator map(Tuple row) { Long id = row.get(qOperator.id); if (id == null) { return null; } Operator operator = new Operator(); operator.id = id; operator.name = nameMapping.map(row); return operator; } }; private final PostgreSQLQueryFactory queryFactory; public OperatorDao(PostgreSQLQueryFactory queryFactory) { this.queryFactory = queryFactory; } @TransactionalWrite @Override public long insertOperator(Operator operator) { return insertOperator(operator, queryFactory.query().select(nextOperatorId).fetchOne()); } @TransactionalWrite public long insertOperator(Operator operator, long operatorId) { SQLInsertClause insert = queryFactory.insert(qOperator); insert.set(qOperator.id, operatorId); nameMapping.populate(operator.name, insert); insert.execute(); return operatorId; } @TransactionalWrite @Override public void updateOperator(long operatorId, Operator operator) { SQLUpdateClause update = queryFactory.update(qOperator); update.where(qOperator.id.eq(operatorId)); nameMapping.populate(operator.name, update); if (update.execute() != 1) { notFound(operatorId); } } private void notFound(long operatorId) { throw new NotFoundException("Operator by id '%s'", operatorId); } @Override @TransactionalRead public Operator getOperator(long operatorId) { return queryFactory.from(qOperator).select(operatorMapping).where(qOperator.id.eq(operatorId)).fetchOne(); } @Override @TransactionalRead public SearchResults<Operator> findOperators(OperatorSearch search) { final PostgreSQLQuery<Operator> qry = queryFactory.from(qOperator).select(operatorMapping); qry.limit(search.getLimit() + 1); qry.offset(search.getOffset()); orderBy(search.getSort(), qry); return SearchResults.of(qry.fetch(), search.getLimit()); } private void orderBy(Sort sort, PostgreSQLQuery qry) { sort = firstNonNull(sort, DEFAULT_SORT); ComparableExpression<String> sortField; switch (firstNonNull(sort.getBy(), DEFAULT_SORT.getBy())) { case "name.fi": sortField = qOperator.nameFi.lower(); break; case "name.sv": sortField = qOperator.nameSv.lower(); break; case "name.en": sortField = qOperator.nameEn.lower(); break; default: throw invalidSortBy(); } if (DESC.equals(sort.getDir())) { qry.orderBy(sortField.desc(), qOperator.id.desc()); } else { qry.orderBy(sortField.asc(), qOperator.id.asc()); } } private ValidationException invalidSortBy() { return new ValidationException(new Violation("SortBy", "sort.by", "Expected one of 'name.fi', 'name.sv' or 'name.en'")); } }