How does the OneToMany annotation with fetch work? - java

Hibernate newbie here.
So I have a User object with many Opportunity objects:
#Entity
public class User implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long userId;
private String firstName;
private String lastName;
private String email;
#OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL, mappedBy="user")
private List<Opportunity> opportunities = new ArrayList();
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
And I have an Opportunity object with a ManyToOne relation with a User:
#Entity
public class Opportunity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long opportunityId;
#ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="userId", nullable=false)
private User user;
private String listingUrl;
private String position;
private String organization;
private String location;
#Column(columnDefinition="text")
private String notes;
#Enumerated(EnumType.STRING)
private Action action;
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
#Temporal(TemporalType.DATE)
private Date dueDate;
All get/set methods are emitted here just to save space.
What I assumed would happen when I used the getOpportunities() method of the User class, was that it would return all the Opportunity objects in the database. I figured that the annotations would pull them automatically for me. However, it did not.
So my question is, do the annotations help retrieve data from the database? How? Or are they just used for creating the database? If I want to get all the opportunities, do I have to write a custom transaction/query?

Related

#GetMapping used to retrive item gives a responce of infinite loop of foreignkey object

i am new in spring boot and i could not find solution for this for a day now.
#GetMapping used to retrive item gives a responce of infinite loop of foreignkey object "user".
why am i getting this infinite loop?
how to fix it?
user object in infinite loop(the problem)
result that i want
item entity
#Entity
public class Item{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long ItemId;
#ManyToOne
#JoinColumn(name = "owner_id")
private User user;
private String ItemName;
// #Column(columnDefinition="text")
private String Description;
private double Price;
private int AvailableQuantity;
private double shippingWeight;
// #Transient
// private MultipartFile Picture;
#Enumerated(value = EnumType.STRING)
private Category category;
#OneToMany(mappedBy = "item")
#JsonIgnore
private List<CartItem> CartItemList;
}
user entity
#Entity
#Table(name = "Utilisateur")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idU;
private String username;
private String password;
private String firstname;
private String lastname;
private String gender;
private Long phone;
private String adress;
#Temporal(TemporalType.DATE)
private Date dateofbirth;
private int rating;
private String email;
public Role role;
private Integer status;
#OneToMany(mappedBy = "user")
private List<Item> ItemList;
}
item service
#Service
public class ItemService implements ItemServiceInterface{
#Autowired
ItemRepository itemrepository;
public Optional<Item> getItemById(long id){
return itemrepository.findById(id);
}
}
item controller
#RestController
public class ItemControl {
#Autowired
ItemServiceInterface itemservice;
#GetMapping("/getitem/{id}")
public Optional<Item> getitembyid(#PathVariable Long id) {
return itemservice.getItemById(id);
}
}
You can use combination of #JsonManagedReference and #JsonBackReference to discourage Jackson from infinite serialization.
#Entity
#Table(name = "Utilisateur")
public class User {
// omitted
#JsonManagedReference
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Item> ItemList;
}
#Entity
public class Item{
// omitted
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "owner_id")
private User user;
}
More details could be found here https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can make use of lazy loading to cut the dependency loop between user and item. However, following that approach might potentially affect other parts of your projects because other codes might use the entity with an assumption that item list in user entity is already eager fetched.
A better way is not return the entity object directly to the REST response. You can define a data model for the rest response and convert the entity to that model in your service class. This way, you can completely control what to return and not to.
Another approach if you still want to use the entity as response: https://www.baeldung.com/spring-data-jpa-named-entity-graphs. This way, you can define when to use the lazy load with each specific query.

Cannot update entity with spring Crude Repository

I am absolute beginner in Spring and need some help. Code below is service layer and is intended to update organisation entity in CrudeRepository, but it only saves new organisation. Just can't imaginate what is wrong.
if (repository.exists(organisation.getId())) {
repository.save(organisation);
}
Organisation.java
#Entity
#Table(name = "organisations")
public class Organisation implements Serializable {
#Id
#GeneratedValue
private long id;
#Version
private Integer version;
#NotNull
#Size(min=3, max=100)
#Basic(optional = false)
#Column(length = 100)
private String name;
#Column(name = "full_name")
private String fullName;
private long inn;
private long kpp;
private String address;
private long phone;
#AssertTrue
#Column(name = "is_active")
private boolean isActive;
Probably your organization object is not managed by spring (it's a raw object, not a managed entity), try finding the object first and then updating the java object and finally save.
Long id = ...
if (repository.exists(id)) {
Organization organization = repository.getOne(id);
// ...
// Update objects field e.g. organization.setName("New Name");
// ...
repository.save(organisation);
}
Note you're using the #Version annotation, when the current version is null spring considers the entity as new. So this field shouldn't be null to update instead insert.

#OneToOne Hibernate with annotations. Can't properly save

I can't make my foreign keys auto generate using hibernate and jpa with annotations. Everything seems ok, The entries are saved in database. All the date come from one form which, when submited creates an User object with ModelAttribute and then saves it in Database.
Here are my beans. Anything else i should add ?
#Entity
#Table(name="adress")
public class Adress implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="adress_id")
private Integer adressId;
#NotBlank(message="The city must be completed")
#Column(name="city")
#Size(min=5,max=30)
private String city;
#NotBlank(message="The street must be completed")
#Column(name="street")
#Size(min=5,max=30)
private String street;
#NotNull(message="The street number must be completed")
#NumberFormat
#Column(name="street_no")
private Integer streetNo;
#OneToOne
#JoinColumn(name="user_id")
private User user;}
and the other one:
#Entity
#Table(name="users")
public class User implements Serializable {
#Id
#Column(name="user_id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer userId;
#NotBlank(message="Username can't be blank")
#Size(min=5,max=30)
#Column(name="username")
private String username;
#NotBlank(message="Password field can't be blank")
#Size(min=5,max=30)
#Column(name="password")
private String password;
#NumberFormat
#NotNull(message="Age field must not be blank")
#Column(name="age")
private Integer age;
#Column(name="message")
#Size(min=0,max=100)
private String message;
#Column(name="date")
#DateTimeFormat(pattern="dd/mm/yyyy")
private Date dateCreated;
#OneToOne(mappedBy="user",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Adress adress;
+getters and setters for them
public void save(T entity){
sessionFactory.getCurrentSession().save(entity);
}
If I understand you correctly and you're trying to get Hibernate to set the foreign key on your related record this might help. Try getting rid of mappedBy and instead specify the JoinColumn. This works for me on a one to many:
The order:
#Entity
#Table(name = "`order`")
public class Order implements Serializable {
#Id
#GeneratedValue
private Long id;
// Order columns...
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "order_id")
private Set<Item> items;
}
The item:
#Entity
#Table(name = "item")
public class Item implements Serializable {
#Id
#GeneratedValue
private Long id;
// Item columns...
#ManyToOne(optional = false)
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false)
private Order order;
}
in adress class
#OneToOne(mappedBy="adress")
private User user;
and in user class
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER,optional=false)
#PrimaryKeyJoinColumn
private Adress adress;

Jpa #OnetoOne relationship

This is my entity class Author
#Entity
#Column(name = "ID")
private Long id;
#Column(name = "LAST1")
private String last;
#Column(name="FIRST1")
private String first;
#Lob
#Column(name = "BIO")
private String bio;
#OneToOne
private AuthorDetail authorId;
getters and setters & zero parameter constructor
this is my other entity AuthorDetail here i have mapped using #OneToOne(optional = false,mappedBy = "authorDetail")
#Column(name = "ID")
private Long id;
#Column(name = "ADDRESS1")
private String address1;
#Column(name="ADDRESS2")
private String address2;
#Column(name = "CITY")
private String city;
#Column(name = "STATE1")
private String state;
#Column(name = "ZIP")
private String zip;
#Column(name = "START_DATE")
#Temporal(TemporalType.DATE)
private Date startDate;
#Lob
#Column(name = "NOTES")
private String notes;
#OneToOne(optional = false,mappedBy = "authorDetail")
private Author authorId;
getters and setters
this my main class
`EntityTransaction entr=em.getTransaction();
entr.begin();
Author author=new Author();
author.setFirst("MD");
author.setLast("RAHMATH");
author.setBio("A Software Developer");
Set detailSet=new HashSet<AuthorDetail>();
AuthorDetail detail=new AuthorDetail();
detail.setAddress1("Address1");
detail.setAddress2("Address2");
detail.setCity("NoMansLand");
detail.setState("ZZ");
detail.setZip("12345");
detail.setNotes("This is test detail");
detailSet.add(detail);
em.persist(author);
entr.commit();`
i am getting exceptions if i try run the program
If you want a single-directional relation, you don't need to write #OneToOne in both class.
From the given piece of code, it appears that you want the bi-direction relationship between Author detail and Author.
For the other side to be aware of the relation (bidirectional), it is required to add the mappedBy attribute to the #OneToOne annotation. This attribute references the (Java) property in the entity that is the owner of the relationship. In your case, in AuthorDetail class you need to add;
#OneToOne(optional = false,mappedBy = "authorId")
private Author authorId;
In mappedBy property, you need to give reference to the entity not class name.
In your Author change the following:
#OneToOne
private AuthorDetail authorId;
To:
#OneToOne
private AuthorDetail authorDetail;

JPA - manytoone cascading

I've got two entities
ServiceDownload.java
#Entity
public class ServiceDownload implements Serializable {
private static final long serialVersionUID = -5336424090042137820L;
#Id
#GeneratedValue
private Long id;
#Length(max = 255)
private String description;
private byte[] download;
private String fileName;
#ManyToOne
private Service service;
Service.java
#Entity
public class Service implements Serializable {
private static final long serialVersionUID = 4520872456865907866L;
#EmbeddedId
private ServiceId id;
#Length(max = 255)
private String servicename;
#Column(columnDefinition = "text")
private String highlightsText;
#Column(columnDefinition = "text")
private String detailsText;
#Column(columnDefinition = "text")
private String productText;
#Column(columnDefinition = "text")
private String dataText;
#ManyToMany(mappedBy = "services")
private Set<Machine> machines;
#OneToMany(targetEntity = ServiceDownload.class)
private List<ServiceDownload> serviceDownloads;
#OneToOne
private ServicePicture servicePicture;
When I create a new ServiceDownload Object and try to persists this I recieve a duplicate key exception. It seems that jpa tries to insert a new service object into the service table. How can I disable this behaviour?
You are using #GeneratedValue annotation for your #Id. According to JPA documentation, you should supply unique identifiers
By default, the application is responsible for supplying and setting entity identifiers (see #Id)
Try using a #SequenceGenerator and a sequence in your database to generate unique identifiers

Categories