de.sainth.recipe.backend.db.repositories.RecipeRepository.java Source code

Java tutorial

Introduction

Here is the source code for de.sainth.recipe.backend.db.repositories.RecipeRepository.java

Source

/*
 * Copyright (c) 2017 sainth (sainth@sainth.de)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package de.sainth.recipe.backend.db.repositories;

import de.sainth.recipe.backend.db.generated.tables.records.DirectionsRecord;
import de.sainth.recipe.backend.db.generated.tables.records.FoodsRecord;
import de.sainth.recipe.backend.db.generated.tables.records.IngredientsRecord;
import de.sainth.recipe.backend.db.generated.tables.records.RecipesRecord;
import de.sainth.recipe.backend.rest.views.*;
import de.sainth.recipe.backend.rest.views.Recipe.Difficulty;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.Record8;
import org.jooq.SelectOnConditionStep;
import org.jooq.impl.EnumConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static de.sainth.recipe.backend.db.generated.tables.Directions.DIRECTIONS;
import static de.sainth.recipe.backend.db.generated.tables.Foods.FOODS;
import static de.sainth.recipe.backend.db.generated.tables.Ingredients.INGREDIENTS;
import static de.sainth.recipe.backend.db.generated.tables.Recipes.RECIPES;
import static de.sainth.recipe.backend.db.generated.tables.Users.USERS;
import static java.lang.Boolean.TRUE;
import static org.jooq.impl.DSL.using;

@Component
public class RecipeRepository {

    private final DifficultyConverter difficultyConverter;
    private final DSLContext create;

    @Autowired
    public RecipeRepository(DSLContext create) {
        this.create = create;
        difficultyConverter = new DifficultyConverter();
    }

    public List<Recipe> findAll() {
        List<Recipe> recipes = selectAllStatement().fetch().stream().map(r -> new Recipe(r.value1(), r.value2(),
                r.value3(), r.value4(), r.value5(), new User(r.value7(), r.value8()), r.value6()))
                .collect(Collectors.toList());
        for (Recipe recipe : recipes) {
            recipe.setIngredients(selectIngredients(recipe.getId()));
            recipe.setDirections(selectDirections(recipe.getId()));
        }
        return recipes;
    }

    public List<Recipe> findAllFor(Long userId) {
        List<Recipe> recipes = selectAllStatement().where(RECIPES.AUTHOR.eq(userId))
                .or(RECIPES.PUBLIC_VISIBLE.eq(TRUE)).fetch().stream().map(r -> new Recipe(r.value1(), r.value2(),
                        r.value3(), r.value4(), r.value5(), new User(r.value7(), r.value8()), r.value6()))
                .collect(Collectors.toList());
        for (Recipe recipe : recipes) {
            recipe.setIngredients(selectIngredients(recipe.getId()));
            recipe.setDirections(selectDirections(recipe.getId()));
        }
        return recipes;
    }

    private SelectOnConditionStep<Record8<Long, String, String, Difficulty, Integer, Boolean, Long, String>> selectAllStatement() {
        return create
                .select(RECIPES.ID, RECIPES.NAME, RECIPES.COMMENT, RECIPES.DIFFICULTY, RECIPES.PORTIONS,
                        RECIPES.PUBLIC_VISIBLE, RECIPES.AUTHOR, USERS.NAME)
                .from(RECIPES).join(USERS).on(USERS.ID.eq(RECIPES.AUTHOR));
    }

    public Recipe findOne(Long id) {
        Record8<Long, String, String, Difficulty, Integer, Boolean, Long, String> record = selectAllStatement()
                .where(RECIPES.ID.eq(id)).fetchOne();
        if (record == null) {
            return null;
        }
        Recipe recipe = new Recipe(record.value1(), record.value2(), record.value3(), record.value4(),
                record.value5(), new User(record.value7(), record.value8()), record.value6());
        recipe.setIngredients(selectIngredients(recipe.getId()));
        recipe.setDirections(selectDirections(recipe.getId()));
        return recipe;
    }

    public void delete(Long id) {
        create.transaction(configuration -> {
            using(configuration).delete(DIRECTIONS).where(DIRECTIONS.RECIPE.eq(id)).execute();
            using(configuration).delete(INGREDIENTS).where(INGREDIENTS.RECIPE.eq(id)).execute();
            using(configuration).delete(RECIPES).where(RECIPES.ID.eq(id)).execute();
        });
    }

    public void deleteAll() {
        create.transaction(configuration -> {
            using(configuration).delete(DIRECTIONS).execute();
            using(configuration).delete(INGREDIENTS).execute();
            using(configuration).delete(RECIPES).execute();
        });
    }

    public Recipe save(Recipe recipe) {
        AtomicReference<Recipe> bu = new AtomicReference<>();
        create.transaction(configuration -> {
            Long id = null;
            if (recipe.getId() != null) {
                id = using(configuration).select(RECIPES.ID).from(RECIPES).where(RECIPES.ID.eq(recipe.getId()))
                        .forUpdate().fetchOneInto(Long.class);
            }
            RecipesRecord recipeRecord;
            List<DirectionsRecord> directionsRecords = new ArrayList<>();
            List<IngredientsRecord> ingredientsRecords = new ArrayList<>();
            if (recipe.getId() == null || id == null) {
                recipeRecord = using(configuration)
                        .insertInto(RECIPES, RECIPES.NAME, RECIPES.COMMENT, RECIPES.DIFFICULTY, RECIPES.PORTIONS,
                                RECIPES.AUTHOR, RECIPES.PUBLIC_VISIBLE)
                        .values(recipe.getName(), recipe.getComment(), recipe.getDifficulty(), recipe.getPortions(),
                                recipe.getAuthor().getId(), recipe.isPublicVisible())
                        .returning().fetchOne();
                for (Ingredient ingredient : recipe.getIngredients()) {
                    ingredientsRecords.add(using(configuration)
                            .insertInto(INGREDIENTS, INGREDIENTS.AMOUNT, INGREDIENTS.CATEGORY, INGREDIENTS.FOOD,
                                    INGREDIENTS.RECIPE)
                            .values(ingredient.getAmount(), ingredient.getCategory(), ingredient.getFood().getId(),
                                    recipeRecord.getId())
                            .returning().fetchOne());
                }
                for (Direction direction : recipe.getDirections()) {
                    directionsRecords.add(using(configuration)
                            .insertInto(DIRECTIONS, DIRECTIONS.POSITION, DIRECTIONS.DESCRIPTION,
                                    DIRECTIONS.DURATION, DIRECTIONS.RECIPE)
                            .values(direction.getPosition(), direction.getDescription(), direction.getDuration(),
                                    recipeRecord.getId())
                            .returning().fetchOne());
                }
            } else {
                recipeRecord = using(configuration).update(RECIPES).set(RECIPES.NAME, recipe.getName())
                        .set(RECIPES.COMMENT, recipe.getComment()).set(RECIPES.DIFFICULTY, recipe.getDifficulty())
                        .set(RECIPES.PORTIONS, recipe.getPortions()).set(RECIPES.AUTHOR, recipe.getAuthor().getId())
                        .set(RECIPES.PUBLIC_VISIBLE, recipe.isPublicVisible()).where(RECIPES.ID.eq(id)).returning()
                        .fetchOne();
                using(configuration).delete(INGREDIENTS).where(INGREDIENTS.RECIPE.eq(id));
                for (Ingredient ingredient : recipe.getIngredients()) {
                    ingredientsRecords.add(using(configuration)
                            .insertInto(INGREDIENTS, INGREDIENTS.AMOUNT, INGREDIENTS.CATEGORY, INGREDIENTS.FOOD,
                                    INGREDIENTS.RECIPE)
                            .values(ingredient.getAmount(), ingredient.getCategory(), ingredient.getFood().getId(),
                                    recipeRecord.getId())
                            .returning().fetchOne());
                }
                using(configuration).delete(DIRECTIONS).where(DIRECTIONS.RECIPE.eq(id));
                for (Direction direction : recipe.getDirections()) {
                    directionsRecords.add(using(configuration)
                            .insertInto(DIRECTIONS, DIRECTIONS.POSITION, DIRECTIONS.DESCRIPTION,
                                    DIRECTIONS.DURATION, DIRECTIONS.RECIPE)
                            .values(direction.getPosition(), direction.getDescription(), direction.getDuration(),
                                    recipeRecord.getId())
                            .returning().fetchOne());
                }
            }
            String username = using(configuration).select(USERS.NAME).from(USERS)
                    .where(USERS.ID.eq(recipeRecord.getAuthor())).fetchOneInto(String.class);

            List<Ingredient> ingredients = ingredientsRecords.stream().map(
                    i -> new Ingredient(i.getAmount(), i.getCategory(), selectFood(configuration, i.getFood())))
                    .collect(Collectors.toList());
            List<Direction> directions = directionsRecords.stream()
                    .map(d -> new Direction(d.getId(), d.getPosition(), d.getDescription(), d.getDuration()))
                    .collect(Collectors.toList());
            bu.set(new Recipe(recipeRecord.getId(), recipeRecord.getName(), recipeRecord.getComment(),
                    recipeRecord.getDifficulty(), recipeRecord.getPortions(), recipeRecord.getPublicVisible(),
                    new User(recipeRecord.getAuthor(), username), ingredients, directions));
        });

        return bu.get();
    }

    private Food selectFood(Configuration configuration, long foodId) {
        FoodsRecord food = using(configuration).selectFrom(FOODS).where(FOODS.ID.eq(foodId)).fetchOne();
        return new Food(food.getId(), food.getName(), food.getPoints(), food.getPointsBaseAmount(),
                food.getBasicUnit());
    }

    private List<Direction> selectDirections(long recipeId) {
        return create.select(DIRECTIONS.ID, DIRECTIONS.POSITION, DIRECTIONS.DESCRIPTION, DIRECTIONS.DURATION)
                .from(DIRECTIONS).where(DIRECTIONS.RECIPE.eq(recipeId)).fetch().stream()
                .map(d -> new Direction(d.value1(), d.value2(), d.value3(), d.value4()))
                .collect(Collectors.toList());
    }

    private List<Ingredient> selectIngredients(long recipeId) {
        return create
                .select(INGREDIENTS.ID, INGREDIENTS.AMOUNT, INGREDIENTS.CATEGORY, FOODS.ID, FOODS.NAME,
                        FOODS.POINTS, FOODS.POINTS_BASE_AMOUNT, FOODS.BASIC_UNIT)
                .from(INGREDIENTS).join(FOODS).on(FOODS.ID.eq(INGREDIENTS.FOOD))
                .where(INGREDIENTS.RECIPE.eq(recipeId)).fetch().stream()
                .map(i -> new Ingredient(i.value1(), i.value2(), i.value3(),
                        new Food(i.value4(), i.value5(), i.value6(), i.value7(), i.value8())))
                .collect(Collectors.toList());
    }

    public void delete(Long id, Long userId) {
        if (create.fetchExists(selectAllStatement().where(RECIPES.ID.eq(id)).and(USERS.ID.eq(userId)))) {
            delete(id);
        }
    }

    public static class DifficultyConverter extends EnumConverter<String, Difficulty> {

        public DifficultyConverter() {
            super(String.class, Difficulty.class);
        }
    }

}