0

I am confused as to how relationships work in entities and what this means with regard to my JPA repository.

I have a class called Loan which stores a list of albums for each loan.

I have a loan repository and an album repository. The album repository is filled with albums when I start the application. The albumId is autogenerated.

When I create a new loan and try to add an album from the repository I get an exception :

Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.library.demo.entity.Album; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.library.demo.entity.Album

If I create a new loan and add a new album on the fly then it works as expected. During the debug I realised that this is because albumId is null when adding a new album on the fly to the loan, presumably because it adds the album to the repository and generates a new albumId when the loan is created.

My album entity looks like this :

@Entity
public class Album implements Serializable {

private static final long serialVersionUID = 0x63A6DA99AA12AAA8L;

@Column @GeneratedValue(strategy = GenerationType.AUTO) @Id private Integer albumId;
@Column (unique=true) private String barcode;
@Column private String band;
@Column private String title;
@Column private String genre;
@Column private Integer year;
@Column private String artworkFilename;
@Column private Boolean enabled;
@Column private Boolean isLoanable;
@Column private Integer numberOfCopies;

@ManyToOne
private Loan loan;

And my loan looks like this :

public class Loan implements Serializable {

private static final long serialVersionUID = 0x62B6DA99AA12AAA8L;

public void setLoanId(Integer loanId) {
    this.loanId = loanId;
}

@Column @GeneratedValue(strategy = GenerationType.AUTO) @Id private Integer loanId;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Album> albums= new ArrayList<>();

@Column private Integer customerId;
@Column private Date dateLoaned;
@Column private Date dateToReturn;
@Column private Boolean expired;

I am also confused as to why the album has to refer back to the loan with a ManyToOne annotation. Why does the album have to refer to the loan?

I am mostly used to relation databases so maybe I am thinking about things in the wrong way. If I can only add new albums to the loan then it defeats the purpose of what I am trying to do.

Reptile
  • 353
  • 1
  • 2
  • 12

3 Answers3

0
    Loan and Album tables have a one-to-many relationship.
    So in Album your mapping should be like below:
    @ManyToOne
    @JoinColumn(name = "loan_id")
    private Loan loan;
Considering loan_id is the primary key of Loan.
And in Loan your mapping should be like below:
@OneToMany(mappedBy = "loan", cascade = CascadeType.ALL)
    private List<Album> albums;

@OneToMany and @ManyToOne defines a one-to-many and many-to-one relationship between 2 entities. @JoinColumn indicates the entity is the owner of the relationship: the corresponding table has a column with a foreign key to the referenced table. mappedBy indicates the entity is the inverse of the relationship
  • Please see my answer. I cannot seem to post a follwup comment due to formatting and size constraints. – Reptile Oct 04 '18 at 08:25
  • loan.setAlbums(albums); after this statement try saving the loan object (loanRepository.save(loan);) – Alok Bavadekar Oct 04 '18 at 08:31
  • This is where it crashes : org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bootLibraryApplication': Invocation of init method failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: uninitialized proxy passed to persist(); nested exception is org.hibernate.PersistentObjectException: uninitialized proxy passed to persist() – Reptile Oct 04 '18 at 08:32
  • Please add the model and dao classes – Alok Bavadekar Oct 04 '18 at 08:42
  • I followed this tutorial to get me started https://dzone.com/articles/java-8-springboot-angularjs-bootstrap-springdata-j https://bitbucket.org/ashish1991/demo/src It suffers the same issue if i creat a Skill Repository and try to add existing skills to the User. – Reptile Oct 04 '18 at 09:08
  • BookCategory bookCategory = bookCategoryRepository.findOne(9); Book newBook = new Book("newBook", bookCategory); bookRepository.save(newBook); Adding book with a existing category works for me. Book Repo: Repository public interface BookRepository extends JpaRepository { } public class Book{ Id GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; ManyToOne JoinColumn(name = "book_category_id") private BookCategory bookCategory; } – Alok Bavadekar Oct 04 '18 at 10:06
  • @Entity @Table(name = "book_category") public class BookCategory { private int id; private String name; @OneToMany(mappedBy = "bookCategory", cascade = CascadeType.ALL) private Set books; – Alok Bavadekar Oct 04 '18 at 10:08
  • It doesn't help me as I don't really see the difference. If you can take a look at the bitbucket source i posted it's a closer example to what i have. – Reptile Oct 04 '18 at 10:56
  • https://stackoverflow.com/questions/23645091/spring-data-jpa-and-hibernate-detached-entity-passed-to-persist-on-manytomany-re I think this is the problem. I will experiement and see if I can solve it. I removed CASCADE and it no longer crashes but the album array is empty. – Reptile Oct 04 '18 at 11:12
0

I have updated my source as you have described and made some changes to the start up.

If I create the loan first and then save it to the loan repository and add the album set afterwards, it no longer crashes.

    Loan loan = new Loan(1, new Date(), calendar.getTime(),null);

    loanRepository.save(loan);

    List<Album> albumList = AlbumImport.getAlbumList();
    albumRepository.save(albumList);


    List<Album> albums = new ArrayList<>();
    albums.add(albumList.get(1));
    albums.add(albumList.get(5));

    loan.setAlbums(albums);

However, when I run my getLoan test the album list is empty

Reptile
  • 353
  • 1
  • 2
  • 12
0

Removing cascade = CascadeType.ALL fixed my problem.

Reptile
  • 353
  • 1
  • 2
  • 12