I am new to hibernate. I have requirement to introduce the delete functionality in hibernate with annotation. ie once we delete the parent, it needs to delete child records ie i need to introduce the ondelete cascade feature. Could you please help how to introduce this feature. please find below is java/entity code.
#Entity
#Table(name = "atfLabel", uniqueConstraints = #UniqueConstraint(columnNames = {"key_", "module_id"}))
#SuppressWarnings("serial")
public class Label {
private long id;
private ModuleImpl module;
private String key;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne(optional=false)
#JoinColumn(name="module_id", nullable=false, updatable=false)
public ModuleImpl getModule() {
return module;
}
public void setModule(ModuleImpl module) {
this.module = module;
}
#Column(name="key_", length=160)
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
#Entity
#Table(name = "atfModule")
#SuppressWarnings("serial")
public class ModuleImpl implements Module {
private long id;
private String name;
private String description;
#OneToMany(mappedBy="module")
#Cascade(CascadeType.DELETE)
private List<Label> label;
#Override
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
#Column(length=200, unique=true, nullable=false)
public String getName() {
return name;
}
#Override
#Column(length=2000)
public String getDescription() {
return description;
}
#Override
public void setName(String name) {
this.name=name;
}
#Override
public void setDescription(String desc) {
this.description=desc;
}
}
#Entity
#Table(name = "atfLabelText",
uniqueConstraints = #UniqueConstraint(columnNames = { "label_id", "tenant_id", "locale" }))
#SuppressWarnings("serial")
public class LabelText {
private long id;
private Label label;
private TenantImpl tenant;
private String locale;
private String text;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
#ManyToOne(optional = false)
#JoinColumn(name = "label_id", nullable = false, updatable = false)
public Label getLabel() {
return label;
}
public String getLocale() {
return locale;
}
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "tenant_id", nullable = true, updatable = false)
public TenantImpl getTenant() {
return tenant;
}
#Column(length = 1500)
public String getText() {
return text;
}
public void setId(long id) {
this.id = id;
}
public void setLabel(Label label) {
this.label = label;
}
public void setLocale(String locale) {
this.locale = locale;
}
public void setTenant(TenantImpl tenant) {
this.tenant = tenant;
}
public void setText(String text) {
this.text = text;
}
}
could you please help me how to do delete functionality and ondelete cascade future.
Thanks
Vijaya Kumar
When you use CascadeType.DELETE:
#OneToMany(mappedBy="module")
#Cascade(CascadeType.DELETE)
private List<Label> label;
It tells Hibernate to navigate the label association and delete persistent instances when an object of ModuleImpl is passed to delete().
Again, if you want to delete any Label persistence instance when it is removed from the label association, you should go with all-delete-orphan:
#OneToMany(orphanRemoval = true, mappedBy = "module")
private List<Label> label;
With all-delete-orphan,any newly instantiated Label becomes persistent if the Label is referenced by a persistent Module. Any persistent Label will be deleted if it’s referenced by an Module when the Module is deleted. And also any persistent Label will be deleted if it’s removed from the label collection of a persistent Module.
Related
I have a next question: while working with Hibernate 3.3.0 run into a situation when I have two tables with one-to-many relationships and I need to get the list of parents. In each entity must be filled the several fields from the parent table and a list of all children mapped in the parent. For the easiest understanding, I give an example. I have two tables with one-to-many relationships: parent is "recipients" and child is "requisites". And I have two classes whose objects are the rows of these tables. Class for the table of recipients:
#Entity
#Table(name = "recipients")
#JsonFilter(value = "recipientsFilter")
public class POJORecipient implements POJO {
private static final long serialVersionUID = 4436819032452218525L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column(name = "client_id")
private long clientId;
#Column
private String inn;
#Column
private String name;
#Column(name = "rcpt_country_code")
private String rcptCountryCode;
#Column(name = "rcpt_passp_ser")
private String rcptPasspSer;
#Column(name = "rcpt_passp_num")
private String rcptPasspNum;
#OneToMany(mappedBy = "recipient", fetch = FetchType.LAZY)
#JsonManagedReference
private Set<POJORequisite> requisites = new HashSet<>();
public POJORecipient(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public long getClientId() {
return clientId;
}
public void setClientId(long clientId) {
this.clientId = clientId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getRcptCountryCode() {
return rcptCountryCode;
}
public void setRcptCountryCode(String rcptCountryCode) {
this.rcptCountryCode = rcptCountryCode;
}
public String getRcptPasspSer() {
return rcptPasspSer;
}
public void setRcptPasspSer(String rcptPasspSer) {
this.rcptPasspSer = rcptPasspSer;
}
public String getRcptPasspNum() {
return rcptPasspNum;
}
public void setRcptPasspNum(String rcptPasspNum) {
this.rcptPasspNum = rcptPasspNum;
}
public Set<POJORequisite> getRequisites() {
return requisites;
}
public void setRequisites(Set<POJORequisite> requisites) {
this.requisites = requisites;
}
}
and for requisites table:
#Entity
#Table(name = "requisites")
public class POJORequisite implements POJO {
private static final long serialVersionUID = -35864567359179960L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column
private String bic;
#Column
private String bill;
#Column
private String comments;
#Column
private String note;
#ManyToOne
#JoinColumn(name = "recipient_id")
#JsonBackReference
private POJORecipient recipient;
public POJORequisite(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getBic() {
return bic;
}
public void setBic(String bic) {
this.bic = bic;
}
public String getBill() {
return bill;
}
public void setBill(String bill) {
this.bill = bill;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public POJORecipient getRecipient() {
return recipient;
}
public void setRecipient(POJORecipient recipient) {
this.recipient = recipient;
}
}
So, I want to select from the recipients only names and all mapped requisites. Consequently, after the selection, I will have a list of POJORecipient objects and in each object filled only the field "name" and set of POJORequisite objects.
As answer of my question I want to discover one of next: how can I do that with help HQL or Criteria API (the second variant is preferable), or understand it is impossible in Hibernate at all, or that this possibility appeared in later versions (also preferably with example). I'm trying to resolve this question for several months now and will be immensely grateful for any help. All clarifications and advices also will be so helpful. Thanks in advance!!!
I create application in Spring, which stores albums, musicians and bands. Album can contain multiple bands and musicians. I created association between Album and Band/Musician. Jet I am unable to delete it. I don't want to delete objects, just the association. I tried to send REST PUT request and setting musicians and bands to null on Album site, yet nothing happens:
{
"id": 2,
"title": "Lulu",
"bands": null,
"musicians": null,
"duration": {
"hours": 1,
"minutes": 20,
"seconds": 4
},
"releaseDate": "31/10/2011",
"coverPath": "https://upload.wikimedia.org/wikipedia/en/4/40/Lou_Reed_and_Metallica_-_Lulu.jpg",
"spotifyPath": null
}
I have created following class and method to link Album and Musician, yet I am unable to "unlink" them:
#RestController
public class AlbumMusicianController {
#Autowired
AlbumRepository albumRepository;
#Autowired
MusicianRepository musicianRepository;
#Transactional
#PostMapping("/musician/{musicianId}/album/{albumId}")
public List<Album> associate(#PathVariable Long musicianId, #PathVariable Long albumId) {
Album album = this.albumRepository.findById(albumId).orElseThrow(() -> new MissingResourceException("Album",
"Album", albumId.toString()));
return this.musicianRepository.findById(musicianId).map((musician) -> { musician.getAlbums().add(album);
return this.musicianRepository.save(musician).getAlbums();
}).orElseThrow(() -> new MissingResourceException("Musician", "Musician", musicianId.toString()));
}
}
Would be thankful for any help.
Below are necessary sources.
Album class:
#Entity
#Table(name="album")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Album {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="title")
private String title;
#ManyToMany(targetEntity = Band.class, mappedBy = "albums")
#JsonSerialize(using = BandsSerializer.class)
private List<Band> bands;
#ManyToMany(targetEntity = Musician.class, mappedBy = "albums")
#JsonSerialize(using = MusiciansSerializer.class)
private List<Musician> musicians;
#Embedded
#Column(name="duration")
private Duration duration;
#Column(name="releasedate")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd/MM/yyyy", timezone="CET")
private Date releaseDate;
#Column(name="coverpath")
private String coverPath;
#Column(name="spotifypath")
private String spotifyPath;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Duration getDuration() {
return duration;
}
public void setDuration(Duration duration) {
this.duration = duration;
}
public Date getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(Date releaseDate) {
this.releaseDate = releaseDate;
}
public String getCoverPath() {
return coverPath;
}
public void setCoverPath(String coverPath) {
this.coverPath = coverPath;
}
public String getSpotifyPath() {
return spotifyPath;
}
public void setSpotifyPath(String spotifyPath) {
this.spotifyPath = spotifyPath;
}
public List<Band> getBands() {
return bands;
}
public void setBands(List<Band> bands) {
this.bands = bands;
}
public List<Musician> getMusicians() {
return musicians;
}
public void setMusicians(List<Musician> musicians) {
this.musicians = musicians;
}
}
Musician class:
#Entity
#Table(name="musician")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Musician {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Column(name="surname")
private String surname;
#Column(name="birthdate")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd/MM/yyyy", timezone="CET")
private Date birthDate;
#Column(name="picturepath")
private String picturePath;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "album_musician",
joinColumns = #JoinColumn(name = "album_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "musician_id",
referencedColumnName = "id"))
private List<Album> albums;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public String getPicturePath() {
return picturePath;
}
public void setPicturePath(String picturePath) {
this.picturePath = picturePath;
}
public List<Album> getAlbums() {
return albums;
}
public void setAlbums(List<Album> albums) {
this.albums = albums;
}
}
Band class:
#Entity
#Table(name="band")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Band {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="name")
private String name;
#Column(name="picturepath")
private String picturePath;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "album_band",
joinColumns = #JoinColumn(name = "album_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "band_id",
referencedColumnName = "id"))
#JsonSerialize(using = AlbumsSerializer.class)
private List<Album> albums;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPicturePath() {
return picturePath;
}
public void setPicturePath(String picturePath) {
this.picturePath = picturePath;
}
public List<Album> getAlbums() {
return albums;
}
public void setAlbums(List<Album> albums) {
this.albums = albums;
}
}
Based on your JSON body I'm going to assume you were sending a PUT request for the Album entity. There were two things that I found missing that got it to work for me after adjusting. I'm not sure if you were avoiding using them for one reason or another.
Cascade rules to cascade changes from Album to its relations.
Proper entity mapping for the join table from Album to its relations.
Not really sure why this was an issue - Hibernate did not seem to throw any exceptions related to this at execution time, but it did not seem to persist things correctly.
Here is an adjusted relation definition for Album's relation to Musician.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="album_musician", joinColumns = #JoinColumn(name = "musician_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "album_id",
referencedColumnName = "id"))
private List<Musician> musicians;
In this format, I was able to cascade changes from Album to Musician. You will have to do something similar for the Band entity to cascade operations from Album to Band.
When I send a GET request in POSTMAN to get all my child entity (Town) the parent entity (Province) is not shown in the JSON response.
This is my controller.
#RequestMapping(value ="api/v1/town",method = RequestMethod.GET)
public ResponseEntity<List<Town>> getAllTowns() {
List<Town> towns = townService.getAllTowns();
if(towns.isEmpty()) {
return new ResponseEntity<List<Town>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<Town>>(towns, HttpStatus.OK);
}
And these are my entities.
Parent Class
#Entity
#Table(name = "PROVINCE")
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PROVINCE_ID")
private long id;
private String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "province", targetEntity = Town.class)
#JsonManagedReference("Province-Town")
private List<Town> towns;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Town> getTowns() {
return towns;
}
public void setTowns(List<Town> towns) {
this.towns = towns;
}
}
Child Class
#Entity
#Table(name = "TOWN")
public class Town {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "TOWN_ID")
private long id;
private String name;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "PROVINCE_ID")
#JsonBackReference("Province-Town")
private Province province;
private long kilometer;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Province getProvince() {
return province;
}
public void setProvince(Province province) {
this.province = province;
}
public long getKilometer() {
return kilometer;
}
public void setKilometer(long kilometer) {
this.kilometer = kilometer;
}
}
The response that I'm getting is like this
{
"id" : 1,
"name" : "Some Town",
"kilometer" : 350
}
What I'm expecting is
{
"id" : 1,
"name" : "Some Town",
"province" : {
//Province data.....
}
"kilometer" : 350
}
I was able to show something like this, but the Objects that I used are not Spring-data-jpa entities, just simple POJOs.
Is there any problem with my Entities? Or is there anything else?
Swap #JsonBackReference and #JsonManagedReference. Basically:
#JsonManagedReference
private Province province;
#JsonBackReference
private List<Town> towns;
I have 2 tables in my database. I have got a Project which can have multiple builds. One build belongs to one project. Everything works fine, except the foreign key in my build table remains null.
Project
#Entity(name="project")
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "projectID")
private Long id;
#Column
#JsonProperty("displayName")
private String name;
#JsonProperty("builds")
#JsonIgnore
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = ("project"))
private Collection<Build> builds;
public Project() {
}
public Project(String name) {
this.name = name;
}
public Collection<Build> getBuilds() {
return builds;
}
public void setBuilds(Collection<Build> builds) {
this.builds = builds;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
Build
#Entity(name = "build")
public class Build implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
#JsonProperty("number")
private Integer number;
#Column
#JsonProperty("url")
private String url;
#JsonBackReference
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "project")
private Project project;
public Build() {
}
public Build(String url, Project project, Integer number) {
this.url = url;
this.project = project;
this.number = number;
}
public Long getId() {
return id;
}
public Integer getNumber() {
return number;
}
public String getUrl() {
return url;
}
public void setId(Long id) {
this.id = id;
}
public void setNumber(Integer number) {
this.number = number;
}
public void setUrl(String url) {
this.url = url;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
Does anyone see the problem?
Using JPA you need explicitly define the Project in your Build object.
Project project = new Project();
Build build = new Build();
build.setProject(project);
project.setBuilds(Collections.singletonList(build));
// now you can persist it
em.persist(project)
From Hibernate documentation:
First, keep in mind that Hibernate does not affect normal Java
semantics. How did we create a link between a Person and an Event in
the unidirectional example? You add an instance of Event to the
collection of event references, of an instance of Person. If you want
to make this link bi-directional, you have to do the same on the other
side by adding a Person reference to the collection in an Event. This
process of "setting the link on both sides" is absolutely necessary
with bi-directional links.
I use Hibernate and
have two entities(City and Region) with OneToMany relation.
the First:
#Entity
#Table(name = "p_region")
public class Region implements Serializable{
#OneToMany(mappedBy = "region",fetch= FetchType.LAZY, cascade = CascadeType.MERGE)
private List<City> citys;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
//++++++++++++++++++++ GETSET
public List<City> getCitys() {
return citys;
}
public void setCitys(List<City> citys) {
this.citys = citys;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and the second one:
#Entity
#Table(name = "p_city")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty(message = "Название не должно быть пустым")
#Length(max = 10, min = 2, message = "Название должно быть менее 2 символов и не
более 100")
private String cityName;
#NotEmpty(message = "Код города не должно быть пустым")
private String cityCode;
#Column(name = "zone")
private Integer zone;
#Basic(optional = true)
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date entryDate = Calendar.getInstance().getTime();
#ManyToOne()
private Region region;
#Basic(optional = true)
private String zip_code;
// GET SET ::::::::::::::::::::::::::::::::::
public Integer getZone() {
return zone;
}
public void setZone(Integer zone) {
this.zone = zone;
}
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
public void delete() {
System.out.println("QQQQQQQQQQQQQQQQQQQQQQ");
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public Date getEntryDate() {
return entryDate;
}
public void setEntryDate(Date entryDate) {
this.entryDate = entryDate;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getZip_code() {
return zip_code;
}
public void setZip_code(String zip_code) {
this.zip_code = zip_code;
}
}
When I try to get simple Object(City) with JSON it returns the cycle:
{"id":577,"region":{"name":"нет региона","id":15,"citys":[{"id":577,"region":
{"name":"нет региона","id":15,"citys":[{"id":577,"region":{"name":"нет
региона","id":15,"citys":[{"id":577,"region":{"name":"нет
региона","id":15,"citys":[{"id":577,"region":{"name":"нет
региона","id":15,"citys":[{"id":577,"region":{"name":"нет
региона","id":15,"citys":[{"id":577,"region":{"name":"нет......so on.
Are there any solutions for this issue?
You need to break the bi-directional relationship between your entity before converting to JSON.
I think there are two options:
Iterate the child collection, e.g. citys in Region and set Region to null. This way, circular dependency would be broken. You my want to add one name mapped attribute regionId in the City so that relational info is still available.
Create another set of POJO objects without circular dependency, copy the values from Entity Objects and then get the JSON using POJO objects.