diff --git a/src/main/java/edu/hsh/dbs2/imdb/Starter.java b/src/main/java/edu/hsh/dbs2/imdb/Starter.java index be851088e4c301ea0be80c21e7c678ec510dfae6..996ce5c72273e58d5fdc237afdadc49575b30fc9 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/Starter.java +++ b/src/main/java/edu/hsh/dbs2/imdb/Starter.java @@ -9,11 +9,7 @@ import javax.swing.*; public class Starter { - /** - * @param args - * @throws Throwable - */ - public static void main(String[] args) throws Throwable { + public static void main(String[] args) { ManagerFactory.instance.setSessionFactory(EntityManagerFactoryUtil.createSessionFactory()); SwingUtilities.invokeLater(() -> new Starter().run()); diff --git a/src/main/java/edu/hsh/dbs2/imdb/entity/CharacterMapper.java b/src/main/java/edu/hsh/dbs2/imdb/entity/CharacterMapper.java index 0d615731a18c78c6534d6b5149055c9bdbf43219..a39d3605c668fb58428a96044963ad0177fdbcf9 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/entity/CharacterMapper.java +++ b/src/main/java/edu/hsh/dbs2/imdb/entity/CharacterMapper.java @@ -33,11 +33,10 @@ public class CharacterMapper implements Mapper<MovieCharacter, CharacterDTO> { return character; } - public MovieCharacter unmapOn(CharacterDTO dto, MovieCharacter character) { + public void unmapOn(CharacterDTO dto, MovieCharacter character) { character.setCharacter(dto.getCharacter()); character.setAlias(dto.getAlias()); var person = character.getCharacter() == null ? null : people.stream().filter(p -> p.getName().equals(dto.getPlayer())).findAny().orElseThrow(); character.setActor(person); - return character; } } diff --git a/src/main/java/edu/hsh/dbs2/imdb/entity/Mapper.java b/src/main/java/edu/hsh/dbs2/imdb/entity/Mapper.java index d50aeac6a079e8ceb47d8149cd79f9510b829cd7..b88f7bf4490cc9616b2b633d9729848f24ccad00 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/entity/Mapper.java +++ b/src/main/java/edu/hsh/dbs2/imdb/entity/Mapper.java @@ -8,6 +8,7 @@ public interface Mapper<T, E> { E map(T t); T unmap(E e); + @SuppressWarnings("unused") default Set<E> mapAll(Collection<T> ts) { return ts.stream().map(this::map).collect(Collectors.toSet()); } diff --git a/src/main/java/edu/hsh/dbs2/imdb/entity/MovieMapper.java b/src/main/java/edu/hsh/dbs2/imdb/entity/MovieMapper.java index 2a57721cd93b3b40aa6cfa0be509605a0f124f69..74dd135cec71446de7e58865ef7252e6b4132424 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/entity/MovieMapper.java +++ b/src/main/java/edu/hsh/dbs2/imdb/entity/MovieMapper.java @@ -11,8 +11,6 @@ import lombok.Setter; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.stream.Collectors; @Setter @@ -50,7 +48,7 @@ public class MovieMapper implements Mapper<Movie, MovieDTO> { return movie; } - public Movie unmapOn(MovieDTO dto, Movie movie) { + public void unmapOn(MovieDTO dto, Movie movie) { if (genres == null && !Util.isNullOrEmpty(dto.getGenres())) throw new IllegalStateException("MovieMapper.unmapOn was called without first setting genres."); movie.setTitle(dto.getTitle()); @@ -60,19 +58,16 @@ public class MovieMapper implements Mapper<Movie, MovieDTO> { .filter(genre -> dto.getGenres().contains(genre.getGenre())) .collect(Collectors.toSet()); movie.setGenres(currentGenres); - return movie; } - - public Movie deepUnmapOn(MovieDTO dto, Movie movie) { + public void deepUnmapOn(MovieDTO dto, Movie movie) { unmapOn(dto, movie); charMapper.setMovie(movie); var mapping = mapMatchingDropDeleted(dto.getCharacters(), movie.getCharacters()); var newCharacters = mapping.entrySet().stream() - .map(insertOrUpdate(charMapper::unmap, charMapper::unmapOn)) + .map(this::insertOrUpdate) .collect(Collectors.toSet()); movie.setCharacters(newCharacters); - return movie; } private MovieCharacter insertOrUpdate(Map.Entry<CharacterDTO, MovieCharacter> entry) { @@ -85,12 +80,6 @@ public class MovieMapper implements Mapper<Movie, MovieDTO> { } } - private Function<Map.Entry<CharacterDTO, MovieCharacter>, MovieCharacter> insertOrUpdate( - Function<CharacterDTO, MovieCharacter> insert, BiFunction<CharacterDTO, MovieCharacter, MovieCharacter> update) { - return entry -> entry.getValue() == null ? insert.apply(entry.getKey()) : update.apply(entry.getKey(), entry.getValue()); - } - - private Map<CharacterDTO, MovieCharacter> mapMatchingDropDeleted(List<CharacterDTO> dtos, Set<MovieCharacter> characters) { return dtos.stream() .map(dto -> matchMCharToDto(dto, characters)) diff --git a/src/main/java/edu/hsh/dbs2/imdb/entity/MovieType.java b/src/main/java/edu/hsh/dbs2/imdb/entity/MovieType.java index 040cbd671b129b24078dbe415b35d44be1488fbb..6607922048775f67232d9dd6078b998f10e223af 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/entity/MovieType.java +++ b/src/main/java/edu/hsh/dbs2/imdb/entity/MovieType.java @@ -1,5 +1,6 @@ package edu.hsh.dbs2.imdb.entity; +@SuppressWarnings("unused") public enum MovieType { V, T, C, G; diff --git a/src/main/java/edu/hsh/dbs2/imdb/entity/Sex.java b/src/main/java/edu/hsh/dbs2/imdb/entity/Sex.java index fb2661f88cd2af0ff848e276cce5a413b7223cfe..5588feb3720d581070d8b4c606811616daf6a9c4 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/entity/Sex.java +++ b/src/main/java/edu/hsh/dbs2/imdb/entity/Sex.java @@ -1,13 +1,5 @@ package edu.hsh.dbs2.imdb.entity; public enum Sex { - f, m; - - public static Sex forName(String name) { - try { - return valueOf(name); - } catch (IllegalArgumentException | NullPointerException ignored) { - return null; - } - } + f, m } diff --git a/src/main/java/edu/hsh/dbs2/imdb/logic/GenreManager.java b/src/main/java/edu/hsh/dbs2/imdb/logic/GenreManager.java index 77cf45e041f7227094a66b691dcb09a888d6403f..4408f488ded14e085a7c361e60d7db2442bada8b 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/logic/GenreManager.java +++ b/src/main/java/edu/hsh/dbs2/imdb/logic/GenreManager.java @@ -3,28 +3,23 @@ package edu.hsh.dbs2.imdb.logic; import edu.hsh.dbs2.imdb.entity.Genre; import jakarta.persistence.EntityManagerFactory; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; public class GenreManager extends Manager { + GenreManager(EntityManagerFactory sessionFactory) { super(sessionFactory); } - /** - * Ermittelt eine vollstaendige Liste aller in der Datenbank abgelegten Genres - * Die Genres werden alphabetisch sortiert zurueckgeliefert. - * @return Alle Genre-Namen als String-Liste - * @throws Exception - */ - public List<String> getGenres() throws Exception { + public List<String> getGenres() { var genres = getGenreImpl(); - return genres.map(Genre::getGenre).sorted().collect(Collectors.toList()); + return genres.stream().map(Genre::getGenre).sorted().collect(Collectors.toList()); } - Stream<Genre> getGenreImpl() { - return query(Genre.class, (cb, cq) -> cq.select(cq.from(Genre.class))).stream(); + Collection<Genre> getGenreImpl() { + return query(Genre.class, (cb, cq) -> cq.select(cq.from(Genre.class))); } } diff --git a/src/main/java/edu/hsh/dbs2/imdb/logic/Manager.java b/src/main/java/edu/hsh/dbs2/imdb/logic/Manager.java index ef70bec7c5a61b4e4ca9ccbdf166af30b69d0717..ceefb4096d579b247672bbb41bc42d7ca72e5d5e 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/logic/Manager.java +++ b/src/main/java/edu/hsh/dbs2/imdb/logic/Manager.java @@ -1,25 +1,65 @@ package edu.hsh.dbs2.imdb.logic; +import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.RollbackException; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; -import lombok.Cleanup; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class Manager { - protected final EntityManagerFactory sessionFactory; + + private final ThreadLocal<EntityManager> localManager; protected Manager(EntityManagerFactory sessionFactory) { - this.sessionFactory = sessionFactory; + this.localManager = ThreadLocal.withInitial(sessionFactory::createEntityManager); } - protected <T> List<T> query(Class<T> type, BiConsumer<CriteriaBuilder, CriteriaQuery<T>> function) { - @Cleanup var entityManager = sessionFactory.createEntityManager(); - var cb = entityManager.getCriteriaBuilder(); + protected <T> List<T> queryNoClose(Class<T> type, BiConsumer<CriteriaBuilder, CriteriaQuery<T>> function) { + var manager = getEntityManager(); + var cb = manager.getCriteriaBuilder(); var cq = cb.createQuery(type); function.accept(cb, cq); - return entityManager.createQuery(cq).getResultList(); + return manager.createQuery(cq).getResultList(); + } + protected <T> List<T> query(Class<T> type, BiConsumer<CriteriaBuilder, CriteriaQuery<T>> function) { + var result = queryNoClose(type, function); + closeEntityManager(); + return result; } + + protected EntityManager getEntityManager() { + if (!localManager.get().isOpen()) + closeEntityManager(); + return localManager.get(); + } + + protected void closeEntityManager() { + var manager = localManager.get(); + if (manager.isOpen()) + manager.close(); + localManager.remove(); + } + + protected void doTransaction(Consumer<EntityManager> content) { + var manager = getEntityManager(); + try { + manager.getTransaction().begin(); + content.accept(manager); + manager.getTransaction().commit(); + } catch (RollbackException e) { + manager.getTransaction().rollback(); + } + } + + protected <T> T mergeIfDetached(T entity) { + var manager = getEntityManager(); + if (!manager.contains(entity)) + return manager.merge(entity); + return entity; + } + } diff --git a/src/main/java/edu/hsh/dbs2/imdb/logic/MovieManager.java b/src/main/java/edu/hsh/dbs2/imdb/logic/MovieManager.java index 4d6d13be186ce10f9c700c4b234314181bfb5308..fa903042da4236d5b8a9243dffd8471ea3b8f27f 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/logic/MovieManager.java +++ b/src/main/java/edu/hsh/dbs2/imdb/logic/MovieManager.java @@ -3,10 +3,9 @@ package edu.hsh.dbs2.imdb.logic; import edu.hsh.dbs2.imdb.entity.Movie; import edu.hsh.dbs2.imdb.entity.MovieMapper; import edu.hsh.dbs2.imdb.logic.dto.MovieDTO; -import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import lombok.Cleanup; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -22,15 +21,7 @@ public class MovieManager extends Manager { personManager = new PersonManager(sessionFactory); } - /** - * Ermittelt alle Filme, deren Filmtitel den Suchstring enthaelt. - * Wenn der String leer ist, sollen alle Filme zurueckgegeben werden. - * Der Suchstring soll ohne Ruecksicht auf Gross/Kleinschreibung verarbeitet werden. - * @param search Suchstring. - * @return Liste aller passenden Filme als MovieDTO - * @throws Exception - */ - public List<MovieDTO> getMovieList(final String search) throws Exception { + public List<MovieDTO> getMovieList(final String search) { var movies = query(Movie.class, (cb, cq) -> { var root = cq.from(Movie.class); cq.select(root); @@ -40,16 +31,7 @@ public class MovieManager extends Manager { return movies.stream().map(mapper::map).collect(Collectors.toList()); } - /** - * Speichert die uebergebene Version des Films neu in der Datenbank oder aktualisiert den - * existierenden Film. - * Dazu werden die Daten des Films selbst (Titel, Jahr, Typ) beruecksichtigt, - * aber auch alle Genres, die dem Film zugeordnet sind und die Liste der Charaktere - * auf den neuen Stand gebracht. - * @param movieDTO Film-Objekt mit Genres und Charakteren. - * @throws Exception - */ - public void insertUpdateMovie(final MovieDTO movieDTO) throws Exception { + public void insertUpdateMovie(final MovieDTO movieDTO) { Optional<Movie> movie = movieDTO.getId() == null ? Optional.empty() : findMovieById(movieDTO.getId()); updateMapper(); if (movie.isPresent()) @@ -59,61 +41,30 @@ public class MovieManager extends Manager { } private void updateMapper() { - var genres = genreManager.getGenreImpl().collect(Collectors.toSet()); - var people = personManager.getPeopleImpl(null).collect(Collectors.toSet()); + var genres = new HashSet<>(genreManager.getGenreImpl()); + var people = new HashSet<>(personManager.getPeopleImpl(null)); mapper.update(genres, people); } - private void insert(MovieDTO dto) throws Exception { - @Cleanup var entityManager = sessionFactory.createEntityManager(); - entityManager.getTransaction().begin(); + private void insert(MovieDTO dto) { var movie = mapper.unmap(dto); - entityManager.persist(movie); - entityManager.getTransaction().commit(); + doTransaction(manager -> manager.persist(movie)); } private void update(Movie movie, MovieDTO dto) { - @Cleanup var entityManager = sessionFactory.createEntityManager(); - entityManager.getTransaction().begin(); - movie = mergeIfDetached(movie, entityManager); - mapper.deepUnmapOn(dto, movie); - entityManager.getTransaction().commit(); + doTransaction(manager -> mapper.deepUnmapOn(dto, mergeIfDetached(movie))); } - private Movie mergeIfDetached(Movie movie, EntityManager entityManager) { - if (!entityManager.contains(movie)) - return entityManager.merge(movie); - return movie; - } - - /** - * Loescht einen Film aus der Datenbank. Es werden auch alle abhaengigen Objekte geloescht, - * d.h. alle Charaktere und alle Genre-Zuordnungen. - * @param movieId - * @throws Exception - */ - public void deleteMovie(final long movieId) throws Exception { + public void deleteMovie(final long movieId) { var movie = findMovieById(movieId); movie.ifPresent(this::delete); } private void delete(Movie movie) { - if (movie.getId() == null) - return; - @Cleanup var entityManager = sessionFactory.createEntityManager(); - entityManager.getTransaction().begin(); - movie = mergeIfDetached(movie, entityManager); - entityManager.remove(movie); - entityManager.getTransaction().commit(); + doTransaction(manager -> manager.remove(mergeIfDetached(movie))); } - /** - * Liefert die Daten eines einzelnen Movies zurück - * @param movieId - * @return - * @throws Exception - */ - public MovieDTO getMovie(final long movieId) throws Exception { + public MovieDTO getMovie(final long movieId) { var movie = findMovieById(movieId); return movie.map(mapper::map).orElse(null); } diff --git a/src/main/java/edu/hsh/dbs2/imdb/logic/PersonManager.java b/src/main/java/edu/hsh/dbs2/imdb/logic/PersonManager.java index ca2f0c83b3a13fb34755493d26954d4716e94bd6..c4eda31dc93ffab84fe2a8b3996cb85b9c9c8476 100644 --- a/src/main/java/edu/hsh/dbs2/imdb/logic/PersonManager.java +++ b/src/main/java/edu/hsh/dbs2/imdb/logic/PersonManager.java @@ -3,9 +3,9 @@ package edu.hsh.dbs2.imdb.logic; import edu.hsh.dbs2.imdb.entity.Person; import jakarta.persistence.EntityManagerFactory; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; public class PersonManager extends Manager { @@ -13,23 +13,17 @@ public class PersonManager extends Manager { super(sessionFactory); } - /** - * Liefert eine Liste aller Personen, deren Name den Suchstring enthaelt. - * @param text Suchstring - * @return Liste mit passenden Personennamen, die in der Datenbank eingetragen sind. - * @throws Exception - */ - public List<String> getPersonList(final String text) throws Exception { + public List<String> getPersonList(final String text) { var people = getPeopleImpl(text); - return people.map(Person::getName).collect(Collectors.toList()); + return people.stream().map(Person::getName).collect(Collectors.toList()); } - Stream<Person> getPeopleImpl(final String text) { + Collection<Person> getPeopleImpl(final String text) { return query(Person.class, (cb, cq) -> { var root = cq.from(Person.class); cq.select(root); if (text != null && text.length() > 0) - cq.where(cb.like(root.get("name"), "%" + text + "%")); - }).stream(); + cq.where(cb.like(cb.upper(root.get("name")), "%" + text.toUpperCase() + "%")); + }); } } diff --git a/src/test/java/edu/hsh/dbs2/imdb/TestDB.java b/src/test/java/edu/hsh/dbs2/imdb/TestDB.java index 44e59d07b00e911353d79eca4d000776f560ca54..a2973159e5615f25082708901cd68000874b85a8 100644 --- a/src/test/java/edu/hsh/dbs2/imdb/TestDB.java +++ b/src/test/java/edu/hsh/dbs2/imdb/TestDB.java @@ -1,6 +1,12 @@ package edu.hsh.dbs2.imdb; -import edu.hsh.dbs2.imdb.entity.*; +import edu.hsh.dbs2.imdb.entity.Genre; +import edu.hsh.dbs2.imdb.entity.Person; +import edu.hsh.dbs2.imdb.entity.Sex; +import edu.hsh.dbs2.imdb.logic.ManagerFactory; +import edu.hsh.dbs2.imdb.logic.MovieManager; +import edu.hsh.dbs2.imdb.logic.dto.CharacterDTO; +import edu.hsh.dbs2.imdb.logic.dto.MovieDTO; import edu.hsh.dbs2.imdb.util.Util; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; @@ -10,17 +16,17 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import java.util.HashMap; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class TestDB { private static EntityManagerFactory sessionFactory; + private static MovieManager movieManager; @BeforeClass public static void setUp() { sessionFactory = Persistence.createEntityManagerFactory("edu.hsh.dbs2.imdb", Util.getTestDataProperty()); + ManagerFactory.instance.setSessionFactory(sessionFactory); + movieManager = ManagerFactory.instance.getMovieManager(); } @AfterClass @@ -53,47 +59,112 @@ public class TestDB { em.getTransaction().commit(); } - @Test - public void testSelectMovie() { - @Cleanup var em = sessionFactory.createEntityManager(); - var movie = em.createQuery("from Movie", Movie.class).getSingleResult(); - assertEquals("Testmovie", movie.getTitle()); - assertEquals(2020, movie.getYear().intValue()); - assertEquals(MovieType.C, movie.getType()); - var genre = movie.getGenres().stream().findAny(); - assertTrue(genre.isPresent()); - assertEquals("Action", genre.get().getGenre()); - var character = movie.getCharacters().stream().findAny(); - assertTrue(character.isPresent()); - assertEquals("character1", character.get().getCharacter()); - assertEquals("Bob", character.get().getAlias()); - assertEquals(1, character.get().getPosition().intValue()); - assertEquals(movie, character.get().getMovie()); - assertEquals("Bob", character.get().getActor().getName()); - assertEquals(Sex.m, character.get().getActor().getSex()); - } - @Test public void testInsertDeleteMovie() { - @Cleanup var em = sessionFactory.createEntityManager(); - var movie = new Movie(); + var movie = new MovieDTO(); movie.setTitle("The Matrix"); - movie.setType(MovieType.C); + movie.setType("C"); movie.setYear(1998); - em.getTransaction().begin(); - em.persist(movie); - em.flush(); - em.getTransaction().commit(); - var cb = em.getCriteriaBuilder(); - var cq = cb.createQuery(Movie.class); - var root = cq.from(Movie.class); - cq.select(root).where(cb.equal(root.get("id"), movie.getId())); - var sameMovie = em.createQuery(cq).getSingleResult(); + movieManager.insertUpdateMovie(movie); + var sameMovie = movieManager.getMovie(10); assertEquals(movie.getTitle(), sameMovie.getTitle()); assertEquals(movie.getType(), sameMovie.getType()); assertEquals(movie.getYear(), sameMovie.getYear()); - em.getTransaction().begin(); - em.remove(sameMovie); - em.getTransaction().commit(); + movieManager.deleteMovie(sameMovie.getId()); + var noMovie = movieManager.getMovie(10); + assertNull(noMovie); + } + + @Test + public void testUpdateMovie() { + var movie = movieManager.getMovie(1); + assertEquals("Testmovie", movie.getTitle()); + assertEquals(2020, movie.getYear()); + assertEquals("C", movie.getType()); + movie.setYear(2021); + movie.setTitle("A Testmovie"); + movie.setType("T"); + movieManager.insertUpdateMovie(movie); + var after = movieManager.getMovie(1); + assertEquals(movie.getTitle(), after.getTitle()); + assertEquals(movie.getYear(), after.getYear()); + assertEquals(movie.getType(), after.getType()); } + + @Test + public void testInsertDeleteGenre() { + var movie = movieManager.getMovie(1); + var genres = movie.getGenres(); + assertEquals(1, genres.size()); + assertEquals("Action", genres.stream().findAny().orElseThrow()); + genres.add("Komödie"); + genres.add("Thriller"); + movieManager.insertUpdateMovie(movie); + var after = movieManager.getMovie(1); + var genresAfter = after.getGenres(); + assertEquals(3, genres.size()); + assertTrue(genresAfter.containsAll(genres)); + genresAfter.remove("Komödie"); + genresAfter.remove("Thriller"); + movieManager.insertUpdateMovie(after); + after = movieManager.getMovie(1); + genresAfter = after.getGenres(); + assertEquals(1, genresAfter.size()); + assertEquals("Action", genresAfter.stream().findAny().orElseThrow()); + } + + @Test + public void testInsertDeleteCharacter() { + var movie = movieManager.getMovie(1); + var newCharacter = new CharacterDTO(); + newCharacter.setCharacter("John"); + newCharacter.setAlias("Johnny"); + newCharacter.setPlayer("Alice"); + movie.getCharacters().add(newCharacter); + movieManager.insertUpdateMovie(movie); + var after = movieManager.getMovie(1); + var characters = after.getCharacters(); + assertEquals(2, characters.size()); + var sameCharacter = characters.stream().filter(mChar -> mChar.getCharacter().equals("John")).findAny().orElseThrow(); + assertEquals(newCharacter.getAlias(), sameCharacter.getAlias()); + assertEquals(newCharacter.getPlayer(), sameCharacter.getPlayer()); + characters.remove(sameCharacter); + movieManager.insertUpdateMovie(after); + after = movieManager.getMovie(1); + characters = after.getCharacters(); + assertEquals(1, characters.size()); + sameCharacter = characters.stream().filter(mChar -> mChar.getCharacter().equals("John")).findAny().orElse(null); + assertNull(sameCharacter); + characters.stream().filter(mChar -> mChar.getCharacter().equals("character1")).findAny().orElseThrow(); + } + + @Test + public void testUpdateCharacter() { + var movie = movieManager.getMovie(1); + var character = movie.getCharacters().stream().findAny().orElseThrow(); + assertEquals("character1", character.getCharacter()); + assertEquals("Bob", character.getAlias()); + assertEquals("Bob", character.getPlayer()); + var newCharacter = new CharacterDTO(); + newCharacter.setCharacter(character.getCharacter()); + newCharacter.setAlias("Mr. President"); + newCharacter.setPlayer("Alice"); + movie.getCharacters().remove(character); + movie.getCharacters().add(newCharacter); + movieManager.insertUpdateMovie(movie); + var after = movieManager.getMovie(1); + assertEquals(1, after.getCharacters().size()); + var charAfter = after.getCharacters().stream().findAny().orElseThrow(); + assertEquals(newCharacter.getCharacter(), charAfter.getCharacter()); + assertEquals(newCharacter.getAlias(), charAfter.getAlias()); + assertEquals(newCharacter.getPlayer(), charAfter.getPlayer()); + after.getCharacters().clear(); + after.getCharacters().add(character); + movieManager.insertUpdateMovie(after); + charAfter = movieManager.getMovie(1).getCharacters().stream().findAny().orElseThrow(); + assertEquals("character1", charAfter.getCharacter()); + assertEquals("Bob", charAfter.getAlias()); + assertEquals("Bob", charAfter.getPlayer()); + } + }