I recently startet Programming a Back-End in Spring Boot. When implementing the Entitys and their Relationships I soon faced Hibernate’s “Object References an Unsaved Transient Instance” Error. I searched for answers in this forum and tried to fix my Cascade Annotation with this Tutorial: https://www.baeldung.com/hibernate-unsaved-transient-instance-error (which is addressing the specific Error). But when I start my Project and fill in Hard Code Data with the init() method I always Face the Error again:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : *.structure.Cart.customer -> *.structure.Customer; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : *.structure.Cart.customer -> *.structure.Customer
#Entity
#Table(name="arti")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Article {
#Id
#GeneratedValue
#Column(name = "id")
private Long articleId;
private String name;
private String manufactor;
private float price;
#ManyToMany
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
#JoinColumn(name = "cart_id")
private Set<Cart> articleCart= new HashSet<>();
#ManyToMany
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.PERSIST})
#JoinColumn(name = "order_id")
private Set<Cart> articleOrder= new HashSet<>();
}
#Entity
#Table(name="cust")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Customer {
#Id
#GeneratedValue
#Column(name = "id")
private Long customerId;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cart_id", referencedColumnName = "id")
private Cart cart;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Order> orders = new HashSet<>();
}
#Entity
#Table(name="cart")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Cart {
#Id
#GeneratedValue
#Column(name = "id")
private Long cartId;
#OneToOne(mappedBy = "cart")
private Customer customer;
#ManyToMany
#JoinColumn(name = "article_id")
private Set<Article> articles= new HashSet<>();
}
#Entity
#Table(name="orde")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Order {
#Id
#GeneratedValue
#Column(name = "id")
private Long orderId;
#ManyToOne
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "customer_id")
private Customer customer;
#ManyToMany
#JoinColumn(name = "article_id")
private Set<Article> articles= new HashSet<>();
}
#SpringBootApplication
public class GqlApplication {
#Autowired
private ArticleRepository articleRepository;
#Autowired
private CartRepository cartRepository;
#Autowired
private CustomerRepository customerRepository;
#Autowired
private OrderRepository orderRepository;
public static void main(String[] args) {
SpringApplication.run(GqlApplication.class, args);
}
#PostConstruct
public void init() {
Article dress = Article.builder()
.name("red dress")
.manufactor("Prada")
.price(20)
.build();
Article hat = Article.builder()
.name("brown hat")
.manufactor("Gucci")
.price(280)
.build();
Set<Article> articles = new HashSet<>();
articles.add(dress);
articles.add(hat);
Customer tom = Customer.builder()
.name("tom")
.address("Am Baum 3")
.build();
Cart cart = Cart.builder()
.customer(tom)
.articles(articles)
.build();
Order order = Order.builder()
.customer(tom)
.articles(articles)
.build();
Set<Order> orders = new HashSet<>();
orders.add(order);
tom.setCart(cart);
tom.setOrders(orders);
articleRepository.save(dress);
articleRepository.save(hat);
cartRepository.save(cart);
customerRepository.save(tom);
orderRepository.save(order);
}
}
Related
I'm getting this a BeanCreationException and I think it's because of my hibernate mappings.
I've been trying to figure out what mapping is wrong but no luck so far!
Customer class :
#Entity
#Data
#Table(name = "customer")
public class Customer {
#Id
#Column(name = "registration_code")
private String registrationCode;
private String name;
private String email;
private String phoneNumber;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer", cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST})
private List<Order> orders;
}
Order class:
#Data
#Entity
#Table(name = "order")
public class Order {
#ManyToOne
#JoinColumn(name = "customer_code")
private Customer customer;
#Id
#OneToMany
#JoinColumn(name = "order_line_id")
private List<OrderLine> orderLines;
#Column(name = "date",columnDefinition = "DATE")
private LocalDate dateOfSubmission;
}
OrderLine class:
#Data
#Entity
#Table(name = "order_line")
public class OrderLine {
#Id
#Column(name = "order_line_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToOne
#JoinColumn(name = "product_code")
private Product product;
private Integer quantity;
}
Product class:
#Data
#Entity
#Table(name = "product")
public class Product {
#Id
private String skuCode;
private String name;
private Long price;
}
It's a spring boot application and I'm using Spring Data JPA
I have these entities where Shop entity is parent:
#Data
#NoArgsConstructor
#Entity
#DynamicUpdate
#Table(name = "Shop", schema = "public")
public class ShopDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String processedStatus;
#OneToMany(mappedBy = "shopDao", cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookDao> bookDaoList;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"shopDao"})
#Table(name = "Book", schema = "public")
public class BookDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
private String author;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "other_id", referencedColumnName = "id")
private OtherDao otherDao;
#ManyToOne
#JoinColumn(name = "shop_id", nullable = false)
private ShopDao shopDao;
}
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = {"bookDao"})
#Table(name = "Other", schema = "public")
public class OtherDao {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String metadata;
#OneToOne(mappedBy = "otherDao", fetch = FetchType.EAGER)
private BookDao bookDao;
}
And these are repos:
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
#Repository
public interface BookRepo extends JpaRepository<BookDao, Long> {
}
#Repository
public interface OtherRepo extends JpaRepository<OtherDao, Long> {
}
When i'm using findAllByProcessedStatus() function, i get BookList inside Shop object correctly, but each Book can't reach their Other objects and i get LazyInitializationException:
screenshot
How do i fix that problem?
Actually, with spring data's #EntityGraph all you need is :
#Repository
public interface ShopRepo extends JpaRepository<ShopDao, Long> {
#EntityGraph(attributePaths = {"bookDaoList.otherDao"})
List<ShopDao> findAllByProcessedStatus(String processedStatus);
}
This is the most convenient way.
For more complex relations, you could define a #NamedEntityGraph, and provide subgraphs, like so.
What I find intriguing, is that the BookDao is the owner of this relation, so I would expect it to be eagerly loaded, since you haven't specified a the Lazy fetch mode explicitly ...
I'm newbie in Spring boot and making first time an experimental app. This app consist on 3 classes / tables:
Account:
has an account numbers that can be call by JournalEntryDetail, so I get #onetoone relation
JournalEntryMaster:
can have many JournalEntryDetail
I use here #OnetoMany annotation
JournalEntryDetail:
can have one JournalEntryMaster
Foreign key to Account, as Account number should be equal b/w JournalEntDetail & Account table
I get an error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'entityManagerFactory' defined in class path resource
[org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation
of init method failed; nested exception is org.hibernate.AnnotationException: #Column(s) not
allowed on a #OneToOne property: com.accounts.entity.JournalEntryMaster.jvNumber
I can understand what does error means but unfortunately I did not find solution, so I came here. Any one could advise will be appreciated.
I copy entities hereunder:
accountnumber
public class Account {
#Id
#GeneratedValue
#Column(name="account_id")
private Long id;
#Column(name="account_number")
private Long accountNumber;
#Column(name="account_name")
private String accountName;
#Enumerated(EnumType.STRING)
#Column(name="dr_or_credit")
private DrOrCrSide drOrCrSide;
#Enumerated(EnumType.STRING)
#Column(name="account_type")
private AccountType accountType;
#ManyToOne(cascade = CascadeType.ALL)
private JournalEntryDetail journalEntryDetail;
JournalEntryMaster
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
public class JournalEntryMaster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#OneToOne
#Column(name="jv_number")
private Long jvNumber;
#Column(name="jv_master_date")
private Date Date;
#Column(name="jv_reference_no")
private String ReferenceNo;
#Column(name="memo")
private String Memo;
#Column(name="posted")
private boolean Posted;
#OneToMany(targetEntity = JournalEntryDetail.class, cascade = CascadeType.ALL)
private Set<JournalEntryDetail> journalEntryDetail = new HashSet<>();
}
JourEntryDetail
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
public class JournalEntryDetail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="account_number")
private Long accountNumber;
#Enumerated(EnumType.STRING)
#Column(name="jv_detail_drcr")
private DrOrCrSide DrCr;
#Column(name="amount")
private Double Amount;
#Column(name="memo")
private String Memo;
#ManyToOne
#JoinColumn(name = "jv", referencedColumnName = "id")
private JournalEntryMaster jv;
#JoinColumn(name = "account_fk_jv", referencedColumnName = "account_number")
#OneToOne(targetEntity = Account.class, cascade = CascadeType.ALL)
private Account account_number;
#JoinColumn(name = "jvnumber_fk", referencedColumnName = "jv_number")
#OneToOne(targetEntity = JournalEntryMaster.class, cascade = CascadeType.ALL)
private JournalEntryMaster jvNumber;
In
public class JournalEntryMaster {
...
#OneToOne <-- remove this annotation
#Column(name="jv_number")
private Long jvNumber;
You use Long for the jvNumber. If this is just a database column and its not related to an entity, don't use #OneToOne. You can remove the annotation and your error #Column(s) not allowed on a #OneToOne property: com.accounts.entity.JournalEntryMaster.jvNumber will disappear.
On the other hand, if it is related to an entity, you have to use JoinColumn instead of Column and use your EntityClass instead of Long.
Remove #OneToOne from JournalEntryMaster above #Column(name="jv_number") private Long jvNumber and relation of JournalEntryDetail with JournalEntryMaster
is #ManyToOne not #OneToOne. you give two relation together for one table in JournalEntryDetail
remove:
#JoinColumn(name = "jvnumber_fk", referencedColumnName = "jv_number")
#OneToOne(targetEntity = JournalEntryMaster.class, cascade = CascadeType.ALL)
private JournalEntryMaster jvNumber;
Here down modified code:
JournalEntryMaster
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
public class JournalEntryMaster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#Column(name="jv_number")
private Long jvNumber;
#Column(name="jv_master_date")
private Date Date;
#Column(name="jv_reference_no")
private String ReferenceNo;
#Column(name="memo")
private String Memo;
#Column(name="posted")
private boolean Posted;
#OneToMany(targetEntity = JournalEntryDetail.class, cascade = CascadeType.ALL)
private Set<JournalEntryDetail> journalEntryDetail = new HashSet<>();
}
JourEntryDetail
#Entity
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
public class JournalEntryDetail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="account_number")
private Long accountNumber;
#Enumerated(EnumType.STRING)
#Column(name="jv_detail_drcr")
private DrOrCrSide DrCr;
#Column(name="amount")
private Double Amount;
#Column(name="memo")
private String Memo;
#ManyToOne
#JoinColumn(name = "jv", referencedColumnName = "id")
private JournalEntryMaster jv;
#JoinColumn(name = "account_fk_jv", referencedColumnName = "account_number")
#OneToOne(targetEntity = Account.class, cascade = CascadeType.ALL)
private Account account_number;
I got a lot of answers and advice, tried changing my code according to these tips.
But, unfortunately, these tips helped only partially.
Now, when creating a new project and creating a new user, I can add the desired user to the set of projects, and the required project will be added to the set of users.
But the relationship between the desired project and the desired user will not appear in the project_user table.
Please help find the answer.
Entity Project
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column
private String description;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<User> projectUserSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_task",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "task_id"))
private Set<Task> projectTaskSet = new HashSet<>();
public void addUserToProject(User user){
this.projectUserSet.add(user);
user.getUserProjectsSet().add(this);
}
public void addTasksToProject(Task task){
this.projectTaskSet.add(task);
task.getTasksProjectSet().add(this);
}
//constructors, hashCode, equals, toString
}
Entity User
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String firstName;
#Column
private String lastName;
#ManyToMany(mappedBy = "projectUserSet", cascade = CascadeType.ALL)
private Set<Project> userProjectsSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_task",
joinColumns = {#JoinColumn(name = "users_id")},
inverseJoinColumns = {#JoinColumn(name = "task_id")})
private Set<Task> userTasksSet = new HashSet<>();
public void addTaskToUser(Task task) {
this.userTasksSet.add(task);
task.getTasksUserSet().add(this);
}
//constructors, hashCode, equals, toString
}
project and user initialization
Project project1 = new Project("Project1", "Project1");
User user1 = new User("User1", "User1");
project1.addUserToProject(user1);
With code shown below, table project_user is populated, verified using H2 console. In order to avoid stack overflow, I had to modify method Project#addTaskToUser as shown below.
Please note that only code relevant to the question, is included.
Normally, issue should be described by some tests. In this case, I added a CommandLineRunner that runs at startup.
CascadeType.ALL is not recommended for many-to-many relations, hence I changed this in code shown below.
Tested using H2 in-memory db.
Project class
#Data
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<MyUser> projectUserSet = new HashSet<>();
public void addUserToProject(User user) {
this.projectUserSet.add(user);
}
}
Project repo
public interface ProjectRepo extends JpaRepository<Project, Long> { }
User class
// cannot use #Data here because it will cause cyclic ref and stack overflow when accessing userProjectsSet
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToMany(mappedBy="projectUserSet")
private Set<Project> userProjectsSet = new HashSet<>();
}
User repo
public interface UserRepo extends JpaRepository<User, Long> {}
In app class
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
CommandLineRunner run(ProjectRepo projectRepo, EntityManagerFactory entityManagerFactory) {
return args -> {
var testUser = new User();
testUser.setFirstName("first-name");
testUser.setLastName("last-name");
var project = new Project();
project.setName("project-name");
project.setDescription("project-description");
project.addUserToProject(testUser);
projectRepo.save(project);
// get saved user and print some properties
var userInDb = entityManagerFactory.createEntityManager().find(User.class, testUser.getId());
System.out.println(userInDb.getFirstName()); // prints "first-name"
System.out.println(userInDb.getUserProjectsSet().size()); // prints "1"
};
}
}
Using spring Boot , When trying to get JPA object into JSON, I keep getting this error :
nested exception is
`org.springframework.http.converter.HttpMessageNotWritableException:
Could not write JSON: Infinite recursion (StackOverflowError);
nested exception is com.fasterxml.jackson.databind.
JsonMappingException: Infinite recursion (StackOverflowError)
(through reference chain: interv.Entities.AppUser["projects"]->org.hibernate.collection.internal.PersistentBag[0]->interv.Entities.Project["appUser"]->interv.Entities.AppUser["projects"]->org.hibernate.collection.internal.Persis`
following some solution in stack overflow i ended by adding the annotation
#JsonIgnoreProperties , so my entity Project looks like this :
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("contrats")
private Collection<Contrat> contrats;
#ManyToOne
#JoinColumn(name = "Id_appUser")
#JsonIgnoreProperties("appUser")
private AppUser appUser;
}
the api restful looks like this :
import java.util.List;
#RestController
public class ProjectsController {
#Autowired
private ProjectRepo projectRepo;
#RequestMapping(path = "/ListProjects", method = RequestMethod.GET)
public List<Project> getProjects(){
return projectRepo.findAll();
}
i tried other annotations but i keep getting the same error :
in ARC extention i get :
200 OK
There was an error parsing JSON data
Unexpected end of JSON input
thank you in advance for your help :) .
EDIT :
the file AppUser.java
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Project> projects = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Intervention> interventions = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Contrat> contrats = new ArrayList<>();
}
The problems occurs because there is an infinite loop when generating the JSON. You can use #JsonManagedReference and #JsonBackReference to solve the Infinite recursion.
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Project implements Serializable{
#Id #GeneratedValue
private long id;
private String intitule;
private String description;
#OneToMany(mappedBy = "project" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("contrats")
private Collection<Contrat> contrats;
#ManyToOne
#JoinColumn(name = "Id_appUser")
#JsonBackReference
private AppUser appUser;
}
AppUser
#Entity
#Data
#AllArgsConstructor #NoArgsConstructor
public class AppUser implements Serializable {
#Id #GeneratedValue
private Long id;
#Column(unique = true)
private String username;
private String password;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<AppRole> roles = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference
private Collection<Project> projects = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Intervention> interventions = new ArrayList<>();
#OneToMany(mappedBy = "appUser" , fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Contrat> contrats = new ArrayList<>();
}