Remote Access To Jpa Entity - java

I'm currently working on system that consists of Java Web app and C# client app. Web app has Java Web Service, which has method that returns entity object of Program class:
#WebMethod(operationName = "getProgram")
public Program getProgram(#WebParam(name = "macAddress") String macAddress){
Device device = DeviceManager.getInstance().getDevice(macAddress);
if(device != null){
return device.getProgram();
}
return null;
}
This return object of type Program which has many properties and relations:
#Entity
#Table(name = "PROGRAM", schema = "APP")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Program.getProgramsByWeather", query = "SELECT p FROM Program p WHERE p.weather = :weather")})
public class Program extends DbEntity implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "LOGO_ID", referencedColumnName = "ID")
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch= FetchType.EAGER)
private Logo logo;
#JoinColumn(name = "WEATHER_ID", referencedColumnName = "ID")
#ManyToOne
private Weather weather;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "program", orphanRemoval = true)
private List<ProgramPlaylist> programPlaylistList = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "program", orphanRemoval = true)
private List<ProgramTicker> programTickerList = new ArrayList<>();
#Column(name = "UPDATED")
private boolean updated;
public Program() {
}
public Program(String name, AppUser owner) {
super(name, owner);
}
public Logo getLogo() {
return logo;
}
public void setLogo(Logo logo) {
this.logo = logo;
}
public Weather getWeather() {
return weather;
}
public void setWeather(Weather weather) {
this.weather = weather;
}
public boolean isUpdated() {
return updated;
}
public void setUpdated(boolean updated) {
this.updated = updated;
}
#XmlElement
public List<ProgramPlaylist> getProgramPlaylistList() {
return programPlaylistList;
}
#XmlElement
public List<ProgramTicker> getProgramTickerList() {
return programTickerList;
}
#Override
public String toString() {
return "Program[ id=" + getId() + " ]";
}
}
Client can get this object and accessing some properties in client app like program.name, which it inherits from DbEntity, but when i try to call something like this:
program.logo.name
client throws NullReferenceException.
Same exception occurs when i try to iterate over the elements of programPlaylistList ArrayList.
I'm assuming that the object itself that is passed through to client isn't fully loaded.
How can i solve this problem, please help?!
EDIT
Ok, so I printed out XML response that client get from service and its populated correctly, but for some reason object fields aren't populated and are mostly null.
Why is this occurring?

Bye default, the fetch strategy for #OneToMany annotations is LAZY, have you tried specifying it to EAGER like in the #oneToOne field (fetch= FetchType.EAGER)?

Related

org.hibernate.HibernateException: identifier of an instance of com.pluralsight.conferencedemo.models.Sessions was altered from 102 to null

Trying to update an item on the database using a PUT request but im getting this error
org.hibernate.HibernateException: identifier of an instance of com.pluralsight.conferencedemo.models.Sessions was altered from 102 to null
Need help
Controller :
#RequestMapping(value = "{id}", method = RequestMethod.PUT)//PUT VS PATCH, you know it
public Sessions update(#PathVariable Long id, #RequestBody Sessions sessions) {
//TODO: Add validation that all attributes are passed in, otherwise return a 400 bad payload
Sessions existingSessions = sessionsRepository.getReferenceById(id);
BeanUtils.copyProperties(sessions, existingSessions, "sessions_id"); //ignoring the primary key to be updated or else it will be null and invalid
return sessionsRepository.saveAndFlush(existingSessions);
}
Model :
#Entity
#JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
#Table(name = "sessions", schema = "public")
public class Sessions {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long session_id;
private String session_name;
#ManyToMany
#JoinTable(
name = "session_speakers",
joinColumns = #JoinColumn (name = "session_id"),
inverseJoinColumns = #JoinColumn(name = "speaker_id") //connection between Session and Speaker Class
)
private List<Speakers> speakers;
public List<Speakers> getSpeaker() {
return speakers;
}
public void setSpeaker(List<Speakers> speaker) {
this.speakers = speaker;
}
public Long getSession_id() {
return session_id;
}
public void setSession_id(Long session_id) {
this.session_id = session_id;
}
public String getSession_name() {
return session_name;
}
public void setSession_name(String session_name) {
this.session_name = session_name; }

Testing Spring Boot Backend Post Request using Postman and Curl

I've been trying to test some simple GET and POST request methods, using Postman and curl through command line.
For some reason, when I try to create a json file and send it through Postman, it saves all the data into the first variable.
I have no idea what's going on. The frontend will deliver everything through JSON files, so if this isn't working, then I want to fix it before finishing up my controller.
Here's my pharmaceutical model:
#Entity
#Table(name = "pharmaceuticals")
public class Pharmaceutical {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "genericName")
private String genericName;
#Column(name = "brandNames")
private ArrayList<String> brandNames;
#Column(name = "strength" )
private String strength;
#Column(name = "quantity")
private Integer quantity;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.MERGE,
CascadeType.REFRESH
})
#JoinTable(name = "pharm_commonuses",
joinColumns = { #JoinColumn(name = "pharmaceutical_id") },
inverseJoinColumns = { #JoinColumn(name = "commonUse_id") })
private Set<CommonUse> commonUses = new HashSet<>();
public Pharmaceutical() {}
public Pharmaceutical(String genericName, ArrayList<String> brandNames, String strength,
Integer quantity) {
this.genericName = genericName;
this.brandNames = brandNames;
this.strength = strength;
this.quantity = quantity;
}
//getters and setters
Here's my controller:
#CrossOrigin(origins = "http://localhost:8081")
#RestController
#RequestMapping("/api")
public class PharmaceuticalController {
#Autowired
PharmaceuticalRepository pharmRepository;
CommonUseRepository comRepository;
#GetMapping("/pharmaceuticals")
public ResponseEntity<List<Pharmaceutical>> getPharmaceuticals(#RequestParam(required = false) String title){
List<Pharmaceutical> pharms = new ArrayList<Pharmaceutical>();
pharmRepository.findAll().forEach(pharms::add);
return new ResponseEntity<>(pharms, HttpStatus.OK);
}
#PostMapping("/pharmaceuticals")
public ResponseEntity<Pharmaceutical> createPharmaceutical(#RequestBody String generic, ArrayList<String> brands, String strength, Integer quant, ArrayList<String> common){
Pharmaceutical newPharm = new Pharmaceutical(generic, brands, strength, quant);
for (String name: common) {
CommonUse com = new CommonUse(name);
comRepository.save(com);
newPharm.getCommonUses().add(com);
}
pharmRepository.save(newPharm);
return new ResponseEntity<>(newPharm, HttpStatus.CREATED);
}
}
Any help would be great!
This is your problem:
#RequestBody String generic
You are saying that the body that comes in, should be placed into this string.
You should build an object representation of the body you are sending in and change it to:
#RequestBody PharmaceuticalRequest generic
and then remove all the other input parameters in the createPharmaceutical function.
Reference:
https://www.baeldung.com/spring-request-response-body##requestbody

Spring Data JPA Many to Many Service Repository Problem

I am trying to add ManyToMany entity to my application. I created entity but cannot implement it.
Actor class
#Entity
#Table(name = "actor")
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false, name = "actor_name")
private String actorName;
#ManyToMany(mappedBy = "actor", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Movie> movie = new HashSet<Movie>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getActorName() { return actorName; }
public void setActorName(String actorName) {
this.actorName = actorName;
}
public Set<Movie> getMovie() {
return movie;
}
public void setMovie(Set<Movie> movie) {
this.movie = movie;
}
}
In movie class I have
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "movie_actor",
joinColumns = {#JoinColumn(name = "movie_id")},
inverseJoinColumns = {#JoinColumn(name = "actor_id")}
)
Set<Actor> actor = new HashSet<Actor>();
........................
public Set<Actor> getActor () {
return actor;
}
public void setActor(Set<Actor> actor){
this.actor = actor;
}
I created my entity just like this but in MovieService;
Actor actor = ActorRepository.findByActorName(movie.getActor().getActorName());
movie.setActor(actor);
This part gives me error. movie.getActor().getActorName() method cannot find. Where do I need to look? In IDE it also says method getActorName and setActorName is never used. I am also adding my ActorRepository and ActorService to closer look to the problem.
ActorRepository
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorName(String actorName);
}
ActorService
#Service
public class ActorService {
private ActorRepository actorRepository;
#Autowired
public ActorService(ActorRepository actorRepository) {
this.actorRepository = actorRepository;
}
public List<Actor> getAllActor() {
return actorRepository.findAll();
}
}
After adding ManyToMany I was using is as OneToMany entity. Services is works for OneToMany. How can I use them for ManyToMany? I need to add multiple actors to my movies. I couldn't find MVC projects for ManyToMany implementation.
You're invoking movie.getActor().getActorName() which basically does a getActorName() on a Set<Actor> object.
You're basically treating the relation as a ManyToOne instead of a OneToMany
You could use the following to fetch the first Actor of the Set
ActorRepository.findByActorName(movie.getActors().iterator().next().getActorName());
But then of course, you don't have all your Actor's names
What you could do is the following
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorNameIn(List<String> actorName);
}
And invoke it that way
ActorRepository.findByActorNameIn(
movie.getActors()
.stream()
.map(Actor::getName)
.collect(Collectors.toList())
);

Transient field is lost when appending new OneToMany entity

I have two entities which are linked via a OneToMany relationship:
#Entity
#Table(name="bookcase")
public class BookCase {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Transient
#Getter #Setter private Long oldId;
/*
https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/
*/
#OneToMany(mappedBy = "bookCase", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Bookshelf> bookShelves = new HashSet<>();
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Set<Bookshelf> getBookShelves() { return bookShelves; }
public void setBookShelves(Set<Bookshelf> bookShelves) { this.bookShelves = bookShelves; }
}
#Entity
#Table(name="bookshelf")
public class Bookshelf {
private static final Logger log = LoggerFactory.getLogger(Bookshelf.class);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Transient
#Getter #Setter private Long oldId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "bookcase_id")
private BookCase bookCase;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public BookCase getBookCase() { return bookCase; }
public void setBookCase(BookCase bookCase) {
this.bookCase = bookCase;
bookCase.getBookShelves().add(this);
}
#Transient
#Setter private OldIdListener oldIdListener;
/*
When the id is saved, listening DTOs can update their ids
*/
#PostPersist
public void triggerOldId() {
log.info("Postpersist triggered for {}", id);
if (oldIdListener != null) {
oldIdListener.updateId(oldId, id);
}
}
}
public interface OldIdListener {
void updateId(long oldId, long newId);
}
The following test fails:
#Test
public void testThatCascadingListenerIsTriggered() {
var mock = mock(OldIdListener.class);
var mock2 = mock(OldIdListener.class);
var mock3 = mock(OldIdListener.class);
var bookcase = new BookCase();
var shelf1 = new Bookshelf();
shelf1.setOldId(-5L);
shelf1.setBookCase(bookcase);
shelf1.setOldIdListener(mock);
var shelf2 = new Bookshelf();
shelf2.setOldId(-6L);
shelf2.setBookCase(bookcase);
shelf2.setOldIdListener(mock2);
var saved = bookCaseRepository.save(bookcase);
verify(mock).updateId(eq(-5L), anyLong());
verify(mock2).updateId(eq(-6L), anyLong());
var savedBookCase = bookCaseRepository.findById(saved.getId()).get();
assertThat(savedBookCase.getBookShelves()).hasSize(2);
var shelf3 = new Bookshelf();
shelf3.setOldId(-10L);
shelf3.setBookCase(savedBookCase);
shelf3.setOldIdListener(mock3);
savedBookCase.getBookShelves().add(shelf3);
bookCaseRepository.save(savedBookCase);
verify(mock3).updateId(eq(-10L), anyLong());
}
mock3 is never called.
When debugging the code, I can see that the transient fields oldId and oldIdListener are set to null when the #PostPersist method is called on object shelf3, not on shelf1 and 2.
I think this is because I am modifying the Set object; but the object is correctly persisted, it just loses all transient fields. This does not happen when the entire tree is persisted for the first time.
Is this the wrong way to insert a new element to a OneToMany set or where is the error here?
I'm using Spring Boot 2.1.
Thanks!
The field which annotation with #Transient will not persist to the database, so if you want it to persist, you must remove #Transient.

Many to Many Hibernate mapping with extra column

I've seen this question on here a few times, however none of the answers fix my issue.
I'm trying to deconstruct a many-to-many relationship down to seperate many-to-one and one-to-many entities so I can add additional columns. From what I have, the main entity saves to the database, but the intermediate does not. If anyone can figure out what's going on I would very much appreciate it. I tried doing this the other way with the primary key composite (aka: #AssociationOverride) but it also did not work. I've scowered the web but cannot find an answer to my issue here.
This is my main entity, MaintOrder:
#Entity
#Table(name="maint_orders")
public class MaintOrder extends PersistedObject implements java.io.Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy="maintOrder")
private Set<ManPowerLine> manPower = new HashSet<ManPowerLine>() ;
public void addManPower(ManPower manPower, Integer quantity, Float price) {
ManPowerLine mpLine = new ManPowerLine();
mpLine.setManPower(manPower);
mpLine.setMaintOrder(this);
mpLine.setManPowerID(manPower.getManPowerID());
mpLine.setMaintOrderID(this.getMaintOrderID());
mpLine.setQuantity(quantity);
mpLine.setPrice(price);
this.manPower.add(mpLine);
// Also add the association object to the employee.
manPower.getMaintOrder().add(mpLine);
}
... getters and setters
}
Here is my secondary entity, ManPower:
#Entity
#Table(name="man_power")
public class ManPower extends PersistedObject implements java.io.Serializable {
...id's, etc
#OneToMany(mappedBy="manPower", fetch = FetchType.EAGER)
private Set<ManPowerLine> maintOrder = new HashSet<ManPowerLine>();
public Set<ManPowerLine> getMaintOrder(){
return maintOrder;
}
public void setMaintOrder(Set<ManPowerLine> maintOrder){
this.maintOrder = maintOrder;
}
... other getters and setters
}
Here is my intermediate entity, ManPowerLine:
#Entity
#Table(name = "man_power_line")
#IdClass(ManPowerLineID.class)
public class ManPowerLine extends PersistedObject implements java.io.Serializable {
#Id
private Long maintOrderID;
#Id
private Long manPowerID;
#Column(name="quantity")
private Integer quantity;
#Column(name="price")
private Float price;
#ManyToOne
#JoinColumn(name = "maintOrderID", updatable = false, insertable = false, referencedColumnName = "maint_order_id")
private MaintOrder maintOrder;
#ManyToOne
#JoinColumn(name = "manPowerID", updatable = false, insertable = false, referencedColumnName = "man_power_id")
private ManPower manPower;
... other getters and setters
}
And my ID entity, ManPowerLineID:
public class ManPowerLineID implements java.io.Serializable {
private Long maintOrderID;
private Long manPowerID;
public Long getMaintOrderID(){
return maintOrderID;
}
public Long getManPowerID(){
return manPowerID;
}
public void setMaintOrderID(Long maintOrderID){
this.maintOrderID = maintOrderID;
}
public void setManPowerID(Long manPowerID){
this.manPowerID = manPowerID;
}
#Override
public int hashCode(){
return (int)(maintOrderID + manPowerID);
}
#Override
public boolean equals(Object obj) {
if( obj instanceof ManPowerLine){
ManPowerLineID otherID = (ManPowerLineID)obj;
boolean hey = (otherID.maintOrderID == this.maintOrderID) && (otherID.manPowerID == this.manPowerID);
return hey;
}
return false;
}
}
Finally the code which utilizes this is as follows:
private void insertObject( ) {
ServiceLocator locator = new ServiceLocator();
SessionFactory sf = locator.getHibernateSessionFactory();
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction();
MaintOrder m = new MaintOrder();
... various setters to m
Set manPowerSet = new HashSet();
for(int i = 0; i < manPowerSet.size(); i++){
ManPower mp = new ManPower();
mp = (ManPower) manPowerSet.iterator().next();
m.addManPower(mp, quantity, cost);
}
sess.saveOrUpdate(m);
tx.commit();
sess.close();
}
Is it possible that I need to use more then just m.addManPower to add to the line? I've tried adding m.setManPowerLine, but it does not change the result.
Anyways I know its a lot of code to look at, but thanks in advance.
Turns out I fixed my own issue on this one. The problem was that I didn't set cascade = CascadeType.ALL, in ALL the right places. Specifically Here:
#OneToMany(mappedBy="manPower", fetch = FetchType.EAGER)
private List<ManPowerLine> maintOrder = new ArrayList<ManPowerLine>();
Should be:
#OneToMany(mappedBy="manPower", cascade = CascadeType.All)
private List<ManPowerLine> maintOrder = new ArrayList<ManPowerLine>();

Categories

Resources