I've stumbled upon a strange problem using a #XmlAdapter. Let me try to sketch the situation:
At the server side I have a class Cows:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Cows implements Serializable {
...a bunch of cow properties like ID, name, ...
#XmlElementWrapper(name = "sampless")
#XmlElement(name = "samples")
#OneToMany(mappedBy = "cowId")
private List<Samples> samplesList;
...a bunch of getters & setters...
}
I have a class Samples:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class Samples implements Serializable {
...a bunch of samples properties like ID, cellcount, ...
#JoinColumn(name = "cow_id", referencedColumnName = "id")
#ManyToOne
#XmlJavaTypeAdapter(CowsAdapter.class)
private Cows cowId;
}
CowsAdapter class:
public class CowsAdapter extends XmlAdapter<Cows, Cows> {
#Override
public Cows unmarshal(Cows v) throws Exception {
return v;
}
#Override
public Cows marshal(Cows v) throws Exception {
return null;
}
}
I include a list of samples when a Cow object is requested by the client as shown above.
Now, at client side, when I try to add a new Sample for a Cow, it marshals correctcly into a xml string (including the Cow object) which is send to the webservice. A new entry is correctly added to the database.
However, when I request the same Cow again (with the samplesList included), the new Sample is not added althou it is in the database? When I restart the glassfish instance, it shows.. Why is this and should I use another approach to avoid this?
Edit: same behaviour happens when deleting samples. It gets deleted from the database but is still included in the response of a Cow request. This has something to do with the cache of glassfish?
Edit2: I found some kind of solution for this. I changed the findById method as follows:
#GET
#Path("cow/{id}")
#Produces({"application/xml"})
public Cows findById(#PathParam("id") Integer id) {
//Cows cow = super.find(id);
final Query qry = getEntityManager().createNamedQuery("Cows.findById", Cows.class);
qry.setParameter("id", id);
qry.setHint("javax.persistence.cache.storeMode", "REFRESH");
Cows cow = (Cows) qry.getSingleResult();
return cow;
}
However, could someone let me know if this is the best approach?
Thanks in advance.
Disabled the Glassfish cache for now by adding a property to persistence.xml
You need to maintain both sides of a relationship. If you create a new Sample you need to add it to its Cow's samples.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
Related
I’m trying to create linked list of nodes with CURRENT/PREVIOUS relation similar to the picture below.
I'm not sure if my solution is the right way to handle this scenario, but to achieve that I created two nodes with a single method to populate new messages as below:
#Builder
#Data
#NoArgsConstructor
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "LATEST")
private Message message;
void newMessage(Message newMessage) {
newMessage.setPrevious(message);
message = newMessage;
}
}
#Builder
#Data
#NoArgsConstructor
public class Message {
#Id
#GeneratedValue
private Long id;
private String text;
#Relationship(type = "PREVIOUS")
private Message previous;
}
I also created a sample code to test this solution:
#SpringBootApplication
public class NewsFeedApplication {
public static void main(String[] args) {
SpringApplication.run(NewsFeedApplication.class, args);
}
#Bean
CommandLineRunner init(PersonRepository personRepository) {
return args -> {
Person personToAdd1 = Person.builder().name("John").build();
personToAdd1.newMessage(Message.builder().text("first message").build());
personToAdd1.newMessage(Message.builder().text("second message").build());
personToAdd1.newMessage(Message.builder().text("third message").build());
personRepository.save(personToAdd1);
personToAdd1.newMessage(Message.builder().text("New message.").build());
personRepository.save(personToAdd1);
};
}
}
I feel like I'm close, but I don't know how to reset the previous CURRENT relation and my solution produces output as:
So the question is:
If my approach is okay, how could I remove previous CURRENT relation.
If this approach is wrong, how could I implement linked list with CURRENT/PREVIOUS relations for nodes correctly.
I found the missing puzzle, i.e. detaching the supplier relation. I don't know why I assumed in advance that this relation should be deleted automatically by the spring data repository "save" method.
Working solution:
public interface PersonRepository extends Neo4jRepository<Supplier, Long> {
#Query("MATCH (n:Person {name: $name})-[r:LATEST]->() DELETE r")
void detachLatestFromPerson(String name);
}
void newMessage(PersonRepository personRepository, Message newMessage) {
personRepository.detachLatestFromPerson(name);
newMessage.setPrevious(message);
message = newMessage;
}
PS. I still have doubts, as I'm not sure if that's a good approach to handle this scenario, so if you know a better solution, go ahead and post it, we can always swap the 'best answer' :)
First, here are my entities.
Player :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
#ManyToOne
#JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
Team :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
#OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
As many topics already stated, you can avoid the StackOverflowExeption in your WebService in many ways with Jackson.
That's cool and all but JPA still constructs an entity with infinite recursion to another entity before the serialization. This is just ugly ans the request takes much longer. Check this screenshot : IntelliJ debugger
Is there a way to fix it ? Knowing that I want different results depending on the endpoint. Examples :
endpoint /teams/{id} => Team={id..., members=[Player={id..., team=null}]}
endpoint /members/{id} => Player={id..., team={id..., members=null}}
Thank you!
EDIT : maybe the question isn't very clear giving the answers I get so I'll try to be more precise.
I know that it is possible to prevent the infinite recursion either with Jackson (#JSONIgnore, #JsonManagedReference/#JSONBackReference etc.) or by doing some mapping into DTO. The problem I still see is this : both of the above are post-query processing. The object that Spring JPA returns will still be (for example) a Team, containing a list of players, containing a team, containing a list of players, etc. etc.
I would like to know if there is a way to tell JPA or the repository (or anything) to not bind entities within entities over and over again?
Here is how I handle this problem in my projects.
I used the concept of data transfer objects, implemented in two version: a full object and a light object.
I define a object containing the referenced entities as List as Dto (data transfer object that only holds serializable values) and I define a object without the referenced entities as Info.
A Info object only hold information about the very entity itself and not about relations.
Now when I deliver a Dto object over a REST API, I simply put Info objects for the references.
Let's assume I deliever a PlayerDto over GET /players/1:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
Whereas the TeamInfo object looks like
public class TeamInfo {
private String teamName;
private String teamColor;
}
compared to a TeamDto
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
This avoids an endless serialization and also makes a logical end for your rest resources as other wise you should be able to GET /player/1/team/player/1/team
Additionally, the concept clearly separates the data layer from the client layer (in this case the REST API), as you don't pass the actually entity object to the interface. For this, you convert the actual entity inside your service layer to a Dto or Info. I use http://modelmapper.org/ for this, as it's super easy (one short method call).
Also I fetch all referenced entities lazily. My service method which gets the entity and converts it to the Dto there for runs inside of a transaction scope, which is good practice anyway.
Lazy fetching
To tell JPA to fetch a entity lazily, simply modify your relationship annotation by defining the fetch type. The default value for this is fetch = FetchType.EAGER which in your situation is problematic. That is why you should change it to fetch = FetchType.LAZY
public class TeamEntity {
#OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
Likewise the Player
public class PlayerEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
When calling your repository method from your service layer, it is important, that this is happening within a #Transactional scope, otherwise, you won't be able to get the lazily referenced entity. Which would look like this:
#Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
In my case I realized I did not need a bidirectional (One To Many-Many To One) relationship.
This fixed my issue:
// Team Class:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// #ManyToOne
// #JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok might also produce this issue. Try adding #ToString and #EqualsAndHashCode if you are using Lombok.
#Data
#Entity
#EqualsAndHashCode(exclude = { "members"}) // This,
#ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
This is a nice guide on infinite recursion annotations https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can use #JsonIgnoreProperties annotation to avoid infinite loop, like this:
#JsonIgnoreProperties("members")
private Team team;
or like this:
#JsonIgnoreProperties("team")
private List<Player> members;
or both.
I have created two classes on client-side and they are stored using GWT JDO.
The parent class looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Park implements Serializable{
#PrimaryKey
#Persistent
private String parkId;
//...
#Persistent(mappedBy = "park", defaultFetchGroup = "true")
private List<Facility> facilityList;
// other stuff
and the child looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Facility implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
#Persistent
private Park park;
// other stuff
And on server side, I have a method to fetch everything:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
ArrayList<Park> parkList = new ArrayList<Park>();
try {
Query q = pm.newQuery(Park.class);
List<Park> parks = (List<Park>) q.execute();
for(Park p:parks)
parkList.add(p);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
When I call this method, it throws an exception:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.datanucleus.store.types.sco.backed.ArrayList' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
I can't figure out whats wrong. Any suggestion is welcome.
I had this problem a while back and it was quite frustrating. And I'm also quite sure your problem isn't because you imported the wrong package. You probably already solved this, but if anyone else need to solve the problem.
Try the below example code:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
List<Park> parkList = null;
try {
Query q = pm.newQuery(Park.class);
parkList = (List<Park>) q.execute();
parkList = pm.detachCopyAll(parkList);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
In the example above I've changed ArrayList to just List (not sure if necessary) and use detachCopyAll to copy the list so it is not connected to the datastore any more, this is the magic that removed the error for me.
There's an annotation that can be used, i quote from the documentation:
You can modify an object after the PersistenceManager has been closed
by declaring the class as "detachable." To do this, add the detachable
attribute to the #PersistenceCapable annotation:
#PersistenceCapable(detachable="true")
Not sure though if this means that you can do, what you originally did, but it's worth a try because I don't think my solution is "pretty".
Change the ArrayList declaration to use Java's ArrayList:
java.util.ArrayList<Park> parkList = new java.util.ArrayList<Park>();
GWT cannot serialize objects that do not have a default constructor and the ArrayList you have imported (org.datanucleus.store.types.sco.backed.ArrayList) doesn't provide a default constructor, making serialization fail.
It is possible that you imported this class by mistake in which case you can just change the import declaration. But if you are using it somewhere else, then you will have to use the full qualifier as shown.
I have been looking into this issue for hours now, probably simple but I don't get it anymore:
I have an entity (Param) which is rendered to json via jax-rs. The entity references another entity (Step).
When writing / reading json, I dont want to see the whole step-entity but merely its id, so I use this code :
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Param implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
long id;
#Column(name = "KEYNAME")
String key;
String value;
#XmlIDREF
Step step;
}
Works perfectly for marshalling.
So any GET-request shows me something the following:
{id: 1,
key: "a",
value: "b",
step: 53
}
But when I post some param to the server, it cant map back the numeric id to a step-entity.
I need to provide the unmarshaller with a custom IDResolver. But how can I configure the unmarshaller???? The Jax-RS servlet is doing the marshalling for me. My code looks like that:
#Path("param")
public class ParamRepresentation {
/**
* Retrieves representation of an instance of ParamRepresentation
* #return an instance of Param
*/
#GET
#Produces("application/json")
#Path("{ID}")
public Param getJson(#PathParam("ID") long id) {
return (Param) ctr.find(id, Param.class);
}
#PUT
#Path("{ID}")
#Consumes("application/json")
#Produces("application/json")
public SuccessMessage updateStep(#PathParam("ID") long id, Param p) {
ctr.update(p);
ParamSuccessMessage sm = new ParamSuccessMessage();
sm.setSuccess(true);
sm.setParam(p);
return sm;
}
}
so how can i configure the unmarshaller ?????
I think you've misunderstood the purpose of IDREF in XML schemas. It's there to allow you to refer to another element that is marked as an ID (i.e., with an #XmlID annotation in JAXB) in the same document. You can't use it to refer to an ID elsewhere in the world; for that you'd use a URI (possibly with a fragment identifier part). To do those in JAXB, you use:
#XmlElement // You might be able to omit this part; it's here for clarity
#XmlSchemaType(name = "anyURI")
public URI exampleField;
You then need to work out whether the URI refers to something you know (i.e., resolve the URI and see if it points into yourself) and deal with the fragment identifier. Or do the more common trick of just using a string and don't worry about trying to magically hook everything up in the binding layer. (That works rather well in practice.)
I've done a similar thing using Jersey and xml representations. I used an xml adapter to symmetrically map between the complete child element and the partial (just id) element.
I would annotate the Step entity in your Param entity as follows:
//javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
#XmlJavaTypeAdapter(PartialStepEntityAdapter.class)
Step step
You would then need to define both the partial Step entity and the Adapter. The PartialStep would be identical to your original step class, but with just the id field.
The PartialStepEntityAdapter would map a Step to a PartialStep when marshalling and a PartialStep to a Step when unmarshalling:
//javax.xml.bind.annotation.adapters.XmlAdapter
public class PartialStepEntityAdapter extends XmlAdapter<PartialStep, Step> {
#Override
public Step unmarshal(PartialStep partialStep) throws Exception {
Step step = new Step();
step.setId(partialStep.getId());
return step;
}
#Override
public PartialStep marshal(Step step) throws Exception {
PartialStep partialStep= new PartialStep();
partialStep.setId(step.getId());
return partialStep;
}
}
Hope that's some help.
I have my domain object, Client, I've got a form on my JSP that is pre-populated with its data, I can take in amended values, and persist the object.
Client has an abstract entity called MarketResearch, which is then extended by one of three more concrete sub-classes.
I have a form to pre-populate some MarketResearch data, but when I make changes and try to persist the Client, it doesn't get saved, can someone give me some pointers on where I've gone wrong?
My 3 domain classes are as follows (removed accessors etc)
public class Client extends NamedEntity
{
#OneToOne
#JoinColumn(name = "MARKET_RESEARCH_ID")
private MarketResearch marketResearch;
...
}
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class MarketResearch extends AbstractEntity
{
...
}
#Entity(name="MARKETRESEARCHLG")
public class MarketResearchLocalGovernment extends MarketResearch
{
#Column(name = "CURRENT_HR_SYSTEM")
private String currentHRSystem;
...
}
This is how I'm persisting
public void persistClient(Client client)
{
if (client.getId() != null)
{
getJpaTemplate().merge(client);
getJpaTemplate().flush();
} else
{
getJpaTemplate().persist(client);
}
}
To summarize, if I change something on the parent object, it persists, but if I change something on the child object it doesn't. Have I missed something blatantly obvious?
I've put a breakpoint right before the persist/merge calls, I can see the updated value on the object, but it doesn't seem to save. I've checked at database level as well, no luck
Thanks
You need to set a proper cascade option on #OneToOne in order to get your operations cascaded:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "MARKET_RESEARCH_ID")
private MarketResearch marketResearch;