Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.

Completed solution #1

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c5f1780
Complete exercise AccountDao
tboychuk Sep 2, 2018
84aa6f6
Merge branch 'master' into exercise/completed
tboychuk Sep 3, 2018
ede6d59
Complete the exercise "JPA persistence.xml configs"
tboychuk Sep 3, 2018
1834ca2
Merge branch 'master' into exercise/completed
tboychuk Sep 3, 2018
365cf52
Merge branch 'master' into exercise/completed
tboychuk Sep 3, 2018
2e8eace
Merge branch 'master' into exercise/completed
tboychuk Sep 3, 2018
98607f1
Fix exercise URLs
tboychuk Sep 3, 2018
72add57
Merge branch 'master' into exercise/completed
tboychuk Sep 3, 2018
285ff6d
Merge branch 'master' into exercise/completed
tboychuk Sep 4, 2018
d48f4b9
Merge branch 'master' into exercise/completed
tboychuk Sep 4, 2018
5aa7af4
Merge branch 'master' into exercise/completed
tboychuk Sep 4, 2018
7585339
Complete "Hello JPA entity" exercise
tboychuk Sep 4, 2018
e8cf5cc
Merge branch 'master' into exercise/completed
tboychuk Sep 5, 2018
d8f4dca
Complete "Employee profile" exercise
tboychuk Sep 5, 2018
2f999ca
Merge branch 'master' into exercise/completed
tboychuk Sep 5, 2018
6fd84dd
Merge branch 'master' into exercise/completed
tboychuk Sep 5, 2018
9a3573b
Complete "Photo comment DAO" exercise
tboychuk Sep 5, 2018
8e94609
Merge branch 'master' into exercise/completed
tboychuk Sep 6, 2018
47258de
Complete "Authors & Books" exercise
tboychuk Sep 6, 2018
9aa8d38
Merge branch 'master' into exercise/completed
tboychuk Sep 6, 2018
26ec6bf
Complete "QueryHelper" exercise
tboychuk Sep 6, 2018
b402233
Merge branch 'master' into exercise/completed
tboychuk Sep 6, 2018
eeba5a6
Complete "Company products" exercise
tboychuk Sep 6, 2018
80f5754
Merge branch 'master' into exercise/completed
tboychuk Sep 7, 2018
e9ae78d
Merge branch 'master' into exercise/completed
tboychuk Sep 9, 2018
5af6bfe
Implement PhotoDaoImpl#addComment()
tboychuk Sep 9, 2018
6af1ac1
Merge branch 'master' into exercise/completed
tboychuk Sep 9, 2018
1994add
Update Photo
tboychuk Sep 9, 2018
55d97d4
Update Author#equals() method
tboychuk Sep 18, 2018
6cf3e8b
Update Author#equals() method
tboychuk Sep 18, 2018
68b2277
Merge branch 'master' into exercise/completed
tboychuk Sep 18, 2018
38c7818
Specify JoinColumn for EmployeeProfile.java
tboychuk Sep 18, 2018
5655f95
Merge branch 'master' into exercise/completed
tboychuk Sep 18, 2018
1e70550
Fix Photo.java
tboychuk Sep 18, 2018
49f3f98
Add a comment to PhotoDaoImpl.java
tboychuk Sep 18, 2018
64e038f
Remove redundant code from PhotoDaoImpl.java
tboychuk Sep 18, 2018
83f970f
Merge branch 'master' into exercise/completed
tboychuk Sep 18, 2018
e0b5bb5
Merge branch 'master' into exercise/completed
tboychuk Oct 22, 2018
d087b6e
Merge branch 'master' into exercise/completed
tboychuk Nov 26, 2018
1310577
Simplified AccountDaoImpl.java
tboychuk Mar 7, 2019
8544333
Upgrade AccountDaoImpl.java
tboychuk Mar 31, 2019
633c8ce
Merge branch 'master' into exercise/completed
tboychuk Mar 31, 2019
a6b6eb8
Add natural key mapping to Book.java
tboychuk Mar 31, 2019
8c5b904
Upgrade Java to 11 and other major dependencies
tboychuk Jun 19, 2019
b2f6f29
Merge branch 'master' into exercise/completed
tboychuk Jun 19, 2019
adeb2e6
Merge branch 'master' into exercise/completed
tboychuk Jun 29, 2019
d35a60f
Merge branch 'master' into exercise/completed
tboychuk Sep 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions account-dao/src/main/java/com/bobocode/dao/AccountDaoImpl.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.bobocode.dao;

import com.bobocode.exception.AccountDaoException;
import com.bobocode.model.Account;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

public class AccountDaoImpl implements AccountDao {
private EntityManagerFactory emf;
@@ -14,32 +19,65 @@ public AccountDaoImpl(EntityManagerFactory emf) {

@Override
public void save(Account account) {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
performWithinPersistenceContext(em -> em.persist(account));
}

@Override
public Account findById(Long id) {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
return performReturningWithinPersistenceContext(entityManager -> entityManager.find(Account.class, id));
}

@Override
public Account findByEmail(String email) {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
return performReturningWithinPersistenceContext(entityManager -> {
TypedQuery<Account> findByEmailQuery
= entityManager.createQuery("select a from Account a where a.email = :email", Account.class);
findByEmailQuery.setParameter("email", email);
return findByEmailQuery.getSingleResult();
});
}

@Override
public List<Account> findAll() {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
return performReturningWithinPersistenceContext(entityManager ->
entityManager.createQuery("select a from Account a", Account.class).getResultList());
}

@Override
public void update(Account account) {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
performWithinPersistenceContext(em -> em.merge(account));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хочу уточнить, Merge вроде как возвращает объект с базы в persistence context, а как он выполняет апдейт с его помощью?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since provided account is in the detached state (it has an id, and is not loaded to the Persistence Context), Hibernate will call the database to load this account by its id first. Then it will copy data from account you provided onto loaded account. So, if the account you provided has some fields changed, these changes will be applied to the loaded account, and since loaded account is changed in the scope of a persistence context, dirty checking will call database to perform an update.

}

@Override
public void remove(Account account) {
throw new UnsupportedOperationException("I don't wanna work without implementation!"); // todo
performWithinPersistenceContext(entityManager -> {
Account mergedAccount = entityManager.merge(account);
entityManager.remove(mergedAccount);
});
}

private void performWithinPersistenceContext(Consumer<EntityManager> entityManagerConsumer) {
performReturningWithinPersistenceContext(entityManager -> {
entityManagerConsumer.accept(entityManager);
return null;
});
}

private <T> T performReturningWithinPersistenceContext(Function<EntityManager, T> entityManagerFunction) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin();
T result;
try {
result = entityManagerFunction.apply(entityManager);
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new AccountDaoException("Error performing dao operation. Transaction is rolled back!", e);
} finally {
entityManager.close();
}
return result;
}
}


66 changes: 39 additions & 27 deletions author-book/src/main/java/com/bobocode/model/Author.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,62 @@
package com.bobocode.model;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.CascadeType;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
* todo:
* - implement no arguments constructor
* - implement getters and setters for all fields
* - implement equals() based on identifier field
* - implement hashCode() that return constant value 31
* - make setter for field {@link Author#books} private
* - initialize field {@link Author#books} as new {@link HashSet}
* - implement a helper {@link Author#addBook(Book)} that establishes a relation on both sides
* - implement a helper {@link Author#removeBook(Book)} that drops a relation on both sides
* <p>
* - configure JPA entity
* - specify table name: "author"
* - configure auto generated identifier
* - configure mandatory column "first_name" for field {@link Author#firstName}
* - configure mandatory column "last_name" for field {@link Author#lastName}
* <p>
* - configure many-to-many relation between {@link Author} and {@link Book}
* - configure cascade operations for this relations {@link CascadeType#PERSIST} and {@link CascadeType#MERGE}
* - configure link (join) table "author_book"
* - configure foreign key column "book_id" references book table
* - configure foreign key column "author_id" references author table
*/
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "author")
public class Author {
@Id
@GeneratedValue
private Long id;

@Column(name = "first_name", nullable = false)
private String firstName;

@Column(name = "last_name", nullable = false)
private String lastName;
private Set<Book> books;

@Setter(AccessLevel.PRIVATE)
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(name = "author_book",
joinColumns = @JoinColumn(name = "author_id"),
inverseJoinColumns = @JoinColumn(name = "book_id")
)
private Set<Book> books = new HashSet<>();

public void addBook(Book book) {
throw new UnsupportedOperationException("Are you kidding me?");
books.add(book);
book.getAuthors().add(this);
}

public void removeBook(Book book) {
throw new UnsupportedOperationException("Are you kidding me?");
books.remove(book);
book.getAuthors().remove(this);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Author)) return false;

Author author = (Author) o;

return Objects.equals(id, author.id);
}

@Override
public int hashCode() {
return 31;
}
}

38 changes: 18 additions & 20 deletions author-book/src/main/java/com/bobocode/model/Book.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
package com.bobocode.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.*;
import org.hibernate.annotations.NaturalId;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
* todo:
* - implement no arguments constructor
* - implement getters and setters for all fields
* - implement equals() and hashCode() based on {@link Book#isbn}
* - make setter for field {@link Book#authors} private
* - initialize field {@link Book#authors} as new {@link HashSet}
* <p>
* - configure JPA entity
* - specify table name: "book"
* - configure auto generated identifier
* - configure mandatory column "name" for field {@link Book#name}
* - configure mandatory unique column "isbn" for field {@link Book#isbn}, it is a natural key candidate
* <p>
* - configure many-to-many relation as mapped on the {@link Author} side
*/
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "isbn")
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String name;

@NaturalId
@Column(nullable = false, unique = true)
private String isbn;
private Set<Author> authors;

@Setter(AccessLevel.PRIVATE)
@ManyToMany(mappedBy = "books")
private Set<Author> authors = new HashSet<>();
}

Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.bobocode.dao;

import com.bobocode.exception.CompanyDaoException;
import com.bobocode.model.Company;
import org.hibernate.Session;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.util.function.Function;

public class CompanyDaoImpl implements CompanyDao {
private EntityManagerFactory entityManagerFactory;
@@ -13,6 +17,28 @@ public CompanyDaoImpl(EntityManagerFactory entityManagerFactory) {

@Override
public Company findByIdFetchProducts(Long id) {
throw new UnsupportedOperationException("I'm still not implemented!");
return readWithinTx(entityManager ->
entityManager
.createQuery("select c from Company c join fetch c.products where c.id = :id", Company.class)
.setParameter("id", id)
.getSingleResult()
);
}

private <T> T readWithinTx(Function<EntityManager, T> entityManagerFunction) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.unwrap(Session.class).setDefaultReadOnly(true);
entityManager.getTransaction().begin();
try {
T queryResult = entityManagerFunction.apply(entityManager);
entityManager.getTransaction().commit();
return queryResult;
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new CompanyDaoException("Error performing read operation", e);
} finally {
entityManager.close();
}
}
}

33 changes: 14 additions & 19 deletions company-products/src/main/java/com/bobocode/model/Company.java
Original file line number Diff line number Diff line change
@@ -6,36 +6,31 @@
import java.util.ArrayList;
import java.util.List;

/**
* todo:
* - implement no arguments constructor
* - implement getters and setters for all fields
* - implement equals() and hashCode() based on identifier field
* - make setter for field {@link Company#products} private
* - initialize field {@link Company#products} as new {@link ArrayList}
* - implement a helper {@link Company#addProduct(Product)} that establishes a relation on both sides
* - implement a helper {@link Company#removeProduct(Product)} that drops a relation on both sides
* <p>
* - configure JPA entity
* - specify table name: "company"
* - configure auto generated identifier
* - configure mandatory column "name" for field {@link Company#name}
* <p>
* - configure one to many relationship as mapped on the child side
*/
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "company")
public class Company {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String name;

@Setter(AccessLevel.PRIVATE)
@OneToMany(mappedBy = "company", fetch = FetchType.LAZY)
private List<Product> products = new ArrayList<>();

public void addProduct(Product product) {
throw new UnsupportedOperationException("I'm still not implemented!");
products.add(product);
product.setCompany(this);
}

public void removeProduct(Product product) {
throw new UnsupportedOperationException("I'm still not implemented!");
products.remove(product);
product.setCompany(null);
}
}
13 changes: 13 additions & 0 deletions company-products/src/main/java/com/bobocode/model/Product.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.bobocode.model;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

/**
* todo:
* - implement no arguments constructor
@@ -21,8 +24,18 @@
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String name;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
private Company company;
}
13 changes: 13 additions & 0 deletions employee-profile/src/main/java/com/bobocode/model/Employee.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

/**
* todo:
* - implement no argument constructor
@@ -20,9 +22,20 @@
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String email;

@Column(nullable = false)
private String fistName;

@Column(nullable = false)
private String lastName;
}

Original file line number Diff line number Diff line change
@@ -4,25 +4,25 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* todo:
* - implement not argument constructor
* - implement getters and setters
* - implement equals and hashCode based on identifier field
*
* - configure JPA entity
* - specify table name: "employee_profile"
* - configure not nullable columns: position, department
*
* - map relation between {@link Employee} and {@link EmployeeProfile} using foreign_key column: "employee_id"
* - configure a derived identifier. E.g. map "employee_id" column should be also a primary key (id) for this entity
*/
import javax.persistence.*;

@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "employee_profile")
public class EmployeeProfile {
@Id
private Long id;

@MapsId
@OneToOne
@JoinColumn(name = "employee_id")
private Employee employee;

@Column(nullable = false)
private String position;

@Column(nullable = false)
private String department;
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
<property name="hibernate.connection.username" value="bobouser"/>
<property name="hibernate.connection.password" value="bobopass"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
16 changes: 9 additions & 7 deletions hello-jpa-entity/src/main/java/com/bobocode/model/Movie.java
Original file line number Diff line number Diff line change
@@ -4,22 +4,24 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* TODO: you're job is to implement mapping for JPA entity {@link Movie}
* - specify id
* - configure id as auto-increment column
* - explicitly specify each column name ("id", "name", "director", and "duration" accordingly)
* - specify not null constraint for fields {@link Movie#name} and {@link Movie#director}
*/
import javax.persistence.*;

@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "movie")
public class Movie {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "name", nullable = false)
private String name;

@Column(name = "director", nullable = false)
private String director;

@Column(name = "duration")
private Integer durationSeconds;
}
21 changes: 12 additions & 9 deletions hello-persistence-xml/src/main/resources/META-INF/persistence.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">

<!--todo: configure a persistence unit name: TuttiFrutti-->
<!--todo: add class Song to this persistence unit-->
<!--todo: configure a hibernate connection url for in-memory H2 database-->
<!--database name: tutti_frutti_db-->
<!--database additional properties: DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false-->
<!--todo: configure a hibernate connection username: little_richard-->
<!--todo: configure a hibernate connection password: rock_n_roll_is_alive-->
<!--todo: configure a hibernate SQL dialect for H2 database-->
<!--todo: configure hibernate generate DDL and create database-->
<persistence-unit name="TuttiFrutti">
<class>com.bobocode.model.Song</class>

<properties>
<property name="hibernate.connection.url" value="jdbc:h2:mem:tutti_frutti_db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.username" value="little_richard"/>
<property name="hibernate.connection.password" value="rock_n_roll_is_alive"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>

</persistence>
32 changes: 22 additions & 10 deletions photo-comment-dao/src/main/java/com/bobocode/dao/PhotoDaoImpl.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,54 @@
package com.bobocode.dao;

import com.bobocode.model.Photo;
import com.bobocode.model.PhotoComment;
import com.bobocode.util.EntityManagerUtil;

import javax.persistence.EntityManagerFactory;
import java.util.List;
import java.util.Objects;

/**
* Please note that you should not use auto-commit mode for your implementation.
*/
public class PhotoDaoImpl implements PhotoDao {
private EntityManagerFactory entityManagerFactory;
private EntityManagerUtil emUtil;

public PhotoDaoImpl(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.emUtil = new EntityManagerUtil(entityManagerFactory);
}

@Override
public void save(Photo photo) {
throw new UnsupportedOperationException("Just do it!"); // todo
Objects.requireNonNull(photo);
emUtil.performWithinTx(entityManager -> entityManager.persist(photo));
}

@Override
public Photo findById(long id) {
throw new UnsupportedOperationException("Just do it!"); // todo
return emUtil.performReturningWithinTx(entityManager -> entityManager.find(Photo.class, id));
}

@Override
@SuppressWarnings("unchecked")
public List<Photo> findAll() {
throw new UnsupportedOperationException("Just do it!"); // todo
return emUtil.performReturningWithinTx(
entityManager -> entityManager.createQuery("select p from Photo p").getResultList()
);
}

@Override
public void remove(Photo photo) {
throw new UnsupportedOperationException("Just do it!"); // todo
Objects.requireNonNull(photo);
emUtil.performWithinTx(entityManager -> {
Photo managedPhoto = entityManager.merge(photo);
entityManager.remove(managedPhoto);
});
}

@Override
public void addComment(long photoId, String comment) {
throw new UnsupportedOperationException("Just do it!"); // todo
emUtil.performWithinTx(entityManager -> {
Photo photoReference = entityManager.getReference(Photo.class, photoId);// does not call database
PhotoComment photoComment = new PhotoComment(comment, photoReference);
entityManager.persist(photoComment);
});
}
}
26 changes: 21 additions & 5 deletions photo-comment-dao/src/main/java/com/bobocode/model/Photo.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.bobocode.model;

import lombok.Getter;
import lombok.Setter;
import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

/**
@@ -23,20 +24,35 @@
* - enable cascade type {@link javax.persistence.CascadeType#ALL} for field {@link Photo#comments}
* - enable orphan removal
*/
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "photo")
public class Photo {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false, unique = true)
private String url;

@Column(nullable = false)
private String description;
private List<PhotoComment> comments;

@Setter(AccessLevel.PRIVATE)
@OneToMany(mappedBy = "photo", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PhotoComment> comments = new ArrayList<>();

public void addComment(PhotoComment comment) {
throw new UnsupportedOperationException("Make me work!");
comments.add(comment);
comment.setPhoto(this);
}

public void removeComment(PhotoComment comment) {
throw new UnsupportedOperationException("Make me work!");
comments.remove(comment);
comment.setPhoto(null);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.bobocode.model;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

/**
@@ -19,11 +22,31 @@
* - map relation between Photo and PhotoComment using foreign_key column: "photo_id"
* - configure relation as mandatory (not optional)
*/
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "photo_comment")
public class PhotoComment {
@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String text;

@Column(nullable = false)
private LocalDateTime createdOn;

@ManyToOne(optional = false)
@JoinColumn(name = "photo_id")
private Photo photo;

public PhotoComment(String text, Photo photo) {
this.text = text;
this.photo = photo;
this.createdOn = LocalDateTime.now();
}
}

15 changes: 14 additions & 1 deletion query-helper/src/main/java/com/bobocode/QueryHelper.java
Original file line number Diff line number Diff line change
@@ -31,6 +31,19 @@ public QueryHelper(EntityManagerFactory entityManagerFactory) {
* @return query result specified by type T
*/
public <T> T readWithinTx(Function<EntityManager, T> entityManagerConsumer) {
throw new UnsupportedOperationException("I'm waiting for you to do your job and make me work ;)"); // todo:
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.unwrap(Session.class).setDefaultReadOnly(true);
entityManager.getTransaction().begin();
try {
T result = entityManagerConsumer.apply(entityManager);
entityManager.getTransaction().commit();
return result;
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new QueryHelperException("Transaction is rolled back.", e);
} finally {
entityManager.close();
}

}
}