Java tutorial
/* * 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); } } }