No entityClass, and because there are multiple in the entityClassSet, it can not be deduced automatically - optaplanner

I'm trying to implement a solution with multiple entity classes, and it fails with the following error message:
no entityClass (null) configured and because there are multiple in the
entityClassSet ([class com.myspace.wla.JobA, class
com.myspace.wla.JobB]), it can not be deduced automatically
This is the solver configuration:
<?xml version="1.0" encoding="UTF-8"?>
<solver xStreamId="1">
<solutionClass>com.myspace.wla.AllocationSolution</solutionClass>
<entityClass>com.myspace.wla.JobA</entityClass>
<entityClass>com.myspace.wla.JobB</entityClass>
<scoreDirectorFactory xStreamId="3"/>
<termination xStreamId="4">
<millisecondsSpentLimit>0</millisecondsSpentLimit>
<secondsSpentLimit>30</secondsSpentLimit>
<minutesSpentLimit>0</minutesSpentLimit>
<hoursSpentLimit>0</hoursSpentLimit>
<daysSpentLimit>0</daysSpentLimit>
</termination>
<constructionHeuristic xStreamId="5">
<constructionHeuristicType>FIRST_FIT</constructionHeuristicType>
<entitySorterManner>NONE</entitySorterManner>
</constructionHeuristic>
<localSearch xStreamId="6">
<unionMoveSelector>
<changeMoveSelector>
<entitySelector>
<entityClass>com.myspace.wla.JobA</entityClass>
</entitySelector>
<valueSelector>
<variableName>Computer</variableName>
<selectionOrder>SORTED</selectionOrder>
</valueSelector>
</changeMoveSelector>
<changeMoveSelector>
<entitySelector>
<entityClass>com.myspace.wla.JobB</entityClass>
</entitySelector>
<valueSelector>
<variableName>Computer</variableName>
<selectionOrder>SORTED</selectionOrder>
</valueSelector>
</changeMoveSelector>
</unionMoveSelector>
</localSearch>
</solver>
This is the solution entity:
package com.myspace.wla;
import java.util.List;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
#PlanningSolution
#javax.xml.bind.annotation.XmlRootElement
#javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD)
public class AllocationSolution implements java.io.Serializable {
static final long serialVersionUID = 1L;
#javax.annotation.Generated({"org.optaplanner.workbench.screens.domaineditor.client.widgets.planner.PlannerDataObjectEditor"})
#javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(org.optaplanner.persistence.jaxb.api.score.buildin.hardsoft.HardSoftScoreJaxbXmlAdapter.class)
#org.kie.api.definition.type.Label("Generated Planner score field")
private org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score;
private java.util.List<com.myspace.wla.Computer> computerList;
private java.util.List<com.myspace.wla.JobA> jobAList;
private int id;
private java.util.List<com.myspace.wla.JobB> jobBList;
public AllocationSolution() {
}
#PlanningScore
public org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore getScore() {
return this.score;
}
public void setScore(
org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score) {
this.score = score;
}
#ValueRangeProvider(id = "computerRange")
#ProblemFactCollectionProperty
public java.util.List<com.myspace.wla.Computer> getComputerList() {
return this.computerList;
}
public void setComputerList(
List<com.myspace.wla.Computer> computerList) {
this.computerList = computerList;
}
#PlanningEntityCollectionProperty
#ValueRangeProvider(id = "jobARange")
public List<com.myspace.wla.JobA> getJobAList() {
return this.jobAList;
}
public void setJobAList(java.util.List<com.myspace.wla.JobA> jobAList) {
this.jobAList = jobAList;
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#PlanningEntityCollectionProperty
#ValueRangeProvider(id = "jobBRange")
public List<com.myspace.wla.JobB> getJobBList() {
return this.jobBList;
}
public void setJobBList(java.util.List<com.myspace.wla.JobB> jobBList) {
this.jobBList = jobBList;
}
public AllocationSolution(
org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore score,
java.util.List<com.myspace.wla.Computer> computerList,
java.util.List<com.myspace.wla.JobA> jobAList, int id,
java.util.List<com.myspace.wla.JobB> jobBList) {
this.score = score;
this.computerList = computerList;
this.jobAList = jobAList;
this.id = id;
this.jobBList = jobBList;
}
}
This is the fact entity:
package com.myspace.wla;
public class Computer implements java.io.Serializable {
static final long serialVersionUID = 1L;
private int id;
private int color;
public Computer() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public int getColor() {
return this.color;
}
public void setColor(int color) {
this.color = color;
}
public Computer(int id, int color) {
this.id = id;
this.color = color;
}
}
This is the first planning entity (the second one is identical with a different name):
package com.myspace.wla;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
#PlanningEntity
public class JobA implements java.io.Serializable {
static final long serialVersionUID = 1L;
private int id;
private com.myspace.wla.Computer computer;
private int color;
public JobA() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#PlanningVariable(valueRangeProviderRefs = {"computerRange"})
public com.myspace.wla.Computer getComputer() {
return this.computer;
}
public void setComputer(com.myspace.wla.Computer computer) {
this.computer = computer;
}
public int getColor() {
return this.color;
}
public void setColor(int color) {
this.color = color;
}
public JobA(int id, com.myspace.wla.Computer computer, int color) {
this.id = id;
this.computer = computer;
this.color = color;
}
}
Thanks,
Eliezer

The problematic part of the configuration is the <constructionHeuristic> phase. You need to use an advanced configuration, that runs one Construction Heuristic phase per each planning entity type. In your case, it should look similar to this:
<constructionHeuristic>
<entitySorterManner>NONE</entitySorterManner>
<valueSorterManner>NONE</valueSorterManner>
<queuedEntityPlacer>
<entitySelector id="jobAEntitySelector">
<cacheType>PHASE</cacheType>
<entityClass>com.myspace.wla.JobA</entityClass>
</entitySelector>
<changeMoveSelector>
<entitySelector mimicSelectorRef="jobAEntitySelector"/>
</changeMoveSelector>
</queuedEntityPlacer>
</constructionHeuristic>
<constructionHeuristic>
<entitySorterManner>NONE</entitySorterManner>
<valueSorterManner>NONE</valueSorterManner>
<queuedEntityPlacer>
<entitySelector id="jobBEntitySelector">
<cacheType>PHASE</cacheType>
<entityClass>com.myspace.wla.JobB</entityClass>
</entitySelector>
<changeMoveSelector>
<entitySelector mimicSelectorRef="jobBEntitySelector"/>
</changeMoveSelector>
</queuedEntityPlacer>
</constructionHeuristic>
Note that it's not possible to use constructionHeuristicType=FIRST_FIT with advanced configuration but using entitySorterManner=NONE and valueSorterManner=NONE is equivalent to FIRST_FIT.
It's also possible to leave entitySorterManner and valueSorterManner out, in which case they'll default to entitySorterManner=DECREASING_DIFFICULTY_IF_AVAILABLE and valueSorterManner=INCREASING_STRENGTH_IF_AVAILABLE.

Related

Spring JPA repository query can't find ID property

I need a spring repository method that lets me get a list of Scene entities using a list of id's. When I try to refer to the Scene Id I get an error, saying it can't find the property called IdScene. I am using a custom query to do this. Is there something wrong with my query?
My Entity is
public class Scene implements Serializable
{
private long id_scene;
private Media media;
private int sceneNumber;
private int version;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id_scene")
public long getIdScene() {
return id_scene;
}
public void setIdScene(long id_scene) {
this.id_scene = id_scene;
}
#ManyToOne
#JoinColumn(name = "id_media")
public Media getMedia() {
return this.media;
}
public void setMedia(Media media) {
this.media = media;
}
private List<Thumbnail> thumbnails = new ArrayList<Thumbnail>();
#OneToMany(mappedBy = "scene", cascade=CascadeType.ALL,
orphanRemoval=true)
#LazyCollection(LazyCollectionOption.FALSE)
public List<Thumbnail> getThumbnails() {
return this.thumbnails;
}
public void setThumbnails(List<Thumbnail> thumbnails) {
this.thumbnails = thumbnails;
}
public void addThumbnail(Thumbnail thumbnail) {
thumbnail.setScene(this);
this.thumbnails.add(thumbnail);
}
private Property property;
#OneToOne(mappedBy="scene", cascade=CascadeType.ALL,
orphanRemoval=true)
#LazyCollection(LazyCollectionOption.FALSE)
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public void addProperty(Property property) {
property.setScene(this);
this.property = property;
}
#Column(name = "sceneNumber")
public int getSceneNumber() {
return sceneNumber;
}
public void setSceneNumber(int sceneNumber) {
this.sceneNumber = sceneNumber;
}
#Column(name = "version")
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
My repository:
public interface SceneRepository extends JpaRepository<Scene, Long> {
public final static String FIND_BY_ID_LIST = "SELECT s"
+ " FROM Scene s WHERE s.IdScene IN (:id)";
#Query(FIND_BY_ID_LIST)
public List<Scene> findByIdScene(#Param("id") List<Long> id);//, Pageable page);
}
try:
"SELECT s FROM Scene s WHERE s.idScene IN (:id)"
note lower case 'i' in 'idScene'"
This is because of the Java Bean naming convention, a property defined as:
public String getWibble() { return wibble; }
public void setWibble(String value) { wibble = value; }
defines wibble and not Wibble

How to combine two DAOs in generic way with only one method in Service layer in Spring Boot

Is it possible to combine two DAOs into one Service method?
I want to create a generic method which will choose correct DAO based on the input parameter. What for now I came up with is the method which will accept Dao from the outside the service object. But this requires to initialize appropriate Dao in the Controller which is a little bit ugly...
Measurement is just an interface for Temperature.java and Humidity.java entities with separate tables on PostgreSQL.
#Service
public class MeasurementService {
#Autowired
private TemperatureDao temperatureDao;
#Autowired
private HumidityDao humidityDao;
public<T extends PagingAndSortingRepository<Measurement, Long>> void insertMeasurementForUser(String username, List<Measurement> measurements, T dao) {
dao.saveAll(measurements);
}
}
TemperatureDao.java
#Repository
public interface TemperatureDao extends PagingAndSortingRepository<Temperature, Long> {
#Query("select u from Temperature u where u.owner = ?1 order by u.id desc")
List<Temperature> findLatestTemperatureForUser(User user, Pageable pageable);
}
HumidityDao.java
#Repository
public interface HumidityDao extends PagingAndSortingRepository<Humidity, Long> {
#Query("select u from Humidity u where u.owner = ?1 order by u.id desc")
List<Humidity> findLatestHumidityForUser(User user, Pageable pageable);
}
Temperature.java
#Entity
#Table(name = "temperature")
public class Temperature implements Measurement {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "th1value")
private Float th1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Temperature() {
}
public Temperature(Float th1Value, LocalDateTime timestamp, User owner) {
this.th1Value = th1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
#Override
public User getOwner() {
return owner;
}
#Override
public void setOwner(User owner) {
this.owner = owner;
}
}
Humidity.java
#Entity
#Table(name = "humidity")
public class Humidity {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "hum1value")
private Float hum1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Humidity() {
}
public Humidity(Float hum1Value, LocalDateTime timestamp, User owner) {
this.hum1Value = hum1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
}
Any ideas?
You could write a Resolver pattern to return needed dao based on your conditions. You service will use the resolver to get the correct dao.
public HellDao implements BaseDao {
public void save();
}
public ByeDao implements BaseDao {
public void save();
}
public DaoResolver {
#Autowired
private helloDao;
#Autowired
private byeDao;
public BaseDao resolve(Object input) {
//based on input return the correct dao
BaseDao resolvedDao = null;
switch(input.enum) {
case Hello:
resolvedDao = helloDao;
break;
case Hello:
resolvedDao = byeDao;
break;
default:
//decide something for default
}
return resolvedDao;
}
}
public class MyService {
#Autowired
private DaoResolver daoResolver;
public Object doSomething() {
BaseDao dao = daoResolver.resolve(someObject);
//you will get HelloDao or ByeDao based on the input
dao.save();
}
}
You can check for the type of measurements using instanceof so you could do it without generics.
public void insertMeasurementForUser(String username, List<Measurement> measurements) {
if(measurements.get(0) instanceof Temperature)
temperatureDao.saveAll(measurements);
else if(measurements.get(0) instanceof Humidity)
humidityDao.saveAll(measurements);
}

Return rows with column not equals null

I have a working Spring Boot application, I'm vey new to JPA/Hibernate and I'm trying to retrieve results having one of the fields containing no null values. Right now I am getting everything including nulls.
It is phone_number_id that I don't want it to be null. I added nullable=false but it looks like it has no effect on retrievals.
What do I need to do to get only columns which have no null occurrences of phone_number_id
My Model
#Entity
#NamedQuery(name="Ad.findAll", query="SELECT a FROM Ad a")
public class Ad implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
#Column(name="ad_content")
private String adContent;
#Column(name="ad_title")
private String adTitle;
#Column(name="ad_url")
private String adUrl;
private Integer age;
#Column(name="post_id")
private Long postId;
//bi-directional many-to-one association to City
#ManyToOne
private City city;
//bi-directional many-to-one association to PhoneNumber
#ManyToOne
#JoinColumn(name="phone_number_id", nullable=false)
private PhoneNumber phoneNumber;
//bi-directional many-to-one association to AdPhoto
#OneToMany(mappedBy="ad")
private List<AdPhoto> adPhotos;
public Ad() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getAdContent() {
return this.adContent;
}
public void setAdContent(String adContent) {
this.adContent = adContent;
}
public String getAdTitle() {
return this.adTitle;
}
public void setAdTitle(String adTitle) {
this.adTitle = adTitle;
}
public String getAdUrl() {
return this.adUrl;
}
public void setAdUrl(String adUrl) {
this.adUrl = adUrl;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public Long getPostId() {
return this.postId;
}
public void setPostId(Long postId) {
this.postId = postId;
}
public City getCity() {
return this.city;
}
public void setCity(City city) {
this.city = city;
}
public PhoneNumber getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(PhoneNumber phoneNumber) {
this.phoneNumber = phoneNumber;
}
public List<AdPhoto> getAdPhotos() {
return this.adPhotos;
}
public void setAdPhotos(List<AdPhoto> adPhotos) {
this.adPhotos = adPhotos;
}
public AdPhoto addAdPhoto(AdPhoto adPhoto) {
getAdPhotos().add(adPhoto);
adPhoto.setAd(this);
return adPhoto;
}
public AdPhoto removeAdPhoto(AdPhoto adPhoto) {
getAdPhotos().remove(adPhoto);
adPhoto.setAd(null);
return adPhoto;
}
My repository
#Repository
public interface AdRepository extends PagingAndSortingRepository<Ad, Long> {
}
and my Service class
#Service
public class AdService {
private final static int PAGESIZE = 3;
#Autowired
AdRepository adRepository;
public Iterable<Ad> findAllAds() {
return adRepository.findAll();
}
public List<Ad> getPage(int pageNumber) {
PageRequest request = new PageRequest(pageNumber - 1, PAGESIZE, Sort.Direction.ASC, "id");
return adRepository.findAll(request).getContent();
}
}
This should work
#Repository
public interface AdRepository extends PagingAndSortingRepository<Ad, Long> {
List<Ad> findByPhoneNumberIsNull();
}
You don't need the NameQuery you get the findAll for free from PagingAndSortingRepository
See the full documentation here
In:
#ManyToOne
#JoinColumn(name="phone_number_id", nullable=false)
private PhoneNumber phoneNumber;
the nullable = false specifies the not null constraint on the DB level.
If you also want to validate the content on the application level you cand use the #NotNull annotation (which I also recommend).
By doing this you do not allow a Ad entity to exist without a phone number. If however the phone number is optional, then use the answer provided by #Essex Boy

Circular Json serializing

i have a javaEE application with two entities which gets persistet in a database. both entities have a bi-directional association.
First Entity:
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Child.class)
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch= FetchType.LAZY)
private Father father;
}
Second Entity
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Father.class)
public class Father implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch= FetchType.LAZY)
private Child child;
}
Both are exposed via ressources like:
#Path("father")
#Stateless
public class FatherResource{
#Context
private UriInfo uri;
#Inject FatherDao fatherDao;
public FatherResource() {
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Father> getFathers() {
//TODO return proper representation object
return fatherDoa.getFathers();
}
}
There is also a DAO which just gets the Father-Object from the database.
The problem is now, that i get a circular json structure.
so something like:
{ "id":"1", "child": {"id":"1", "father": {"id":"1", "child":{"id":"1", [...]
I just want to see the child once.
I tried to use:
#JsonIgnoreProperties({"child"}) //above the class
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id") //above the class
#JsonIgnore //above the property
what i want to get is:
{ "id":"1", "child": {"id":"1"}}
I'm using
JDK 7
JaxRS
Jackson
Hibernate/Eclipselink
Glassfish 4.0
another test which doesn't work:
#Entity
#JsonSerialize
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="id", scope=Object.class)
public class Father implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(fetch= FetchType.LAZY)
private Child child;
}
ApplicationConfig.java:
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
// following code can be used to customize Jersey 2.0 JSON provider:
try {
Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature");
} //...
//..
beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>
You could write a loop that sets father to null before returning the list. That way you won't give a circular data structure to the JSON serializer.
This solution may not look very clean, but it works and since nobody else has answered this question yet I thought it was worth mentioning.
Here is an example, which works and addresses your problem. Jackson is able to serialize your recursions. Have a look at this:
3 POJOs (A, B, C) and a test. Tested with jackson 2.2.3.
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
public class RecClassA {
private RecClassA mySelf;
private RecClassB b;
private String value = "AV";
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public RecClassA getMySelf() {
return mySelf;
}
public void setMySelf(RecClassA mySelf) {
this.mySelf = mySelf;
}
public RecClassB getB() {
return b;
}
public void setB(RecClassB b) {
this.b = b;
}
}
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
public class RecClassB {
private String value = "BV";
private RecClassC c;
private RecClassA a;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public RecClassC getC() {
return c;
}
public void setC(RecClassC c) {
this.c = c;
}
public RecClassA getA() {
return a;
}
public void setA(RecClassA a) {
this.a = a;
}
}
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
public class RecClassC {
private String value = "CV";
private RecClassA a;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public RecClassA getA() {
return a;
}
public void setA(RecClassA a) {
this.a = a;
}
}
import org.testng.annotations.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ObjectMapperRecursionTest {
private ObjectMapper om = new ObjectMapper();
#Test
public void simpleWriteTest() throws JsonProcessingException{
RecClassA a = new RecClassA();
String result = om.writeValueAsString(a);
System.out.println("simpleWriteTest: " + result);
}
#Test
public void simpleRecursionWriteTest() throws JsonProcessingException{
RecClassA a = new RecClassA();
a.setMySelf(a);
String result = om.writeValueAsString(a);
System.out.println("simpleRecursionWriteTest: " + result);
}
#Test
public void abbaRecursionWriteTest() throws JsonProcessingException{
RecClassA a = new RecClassA();
RecClassB b = new RecClassB();
a.setB(b);
b.setA(a);
String result = om.writeValueAsString(a);
System.out.println("abbaRecursionWriteTest: " + result);
}
#Test
public void abcaRecursionWriteTest() throws JsonProcessingException{
RecClassA a = new RecClassA();
RecClassB b = new RecClassB();
RecClassC c = new RecClassC();
a.setB(b);
b.setC(c);
c.setA(a);
String result = om.writeValueAsString(c);
System.out.println("abcaRecursionWriteTest: " + result);
}
}
Here the result of the test:
abbaRecursionWriteTest: {"#id":1,"mySelf":null,"b":{"#id":2,"value":"BV","c":null,"a":1},"value":"AV"}
abcaRecursionWriteTest: {"#id":1,"value":"CV","a":{"#id":2,"mySelf":null,"b":{"#id":3,"value":"BV","c":1,"a":null},"value":"AV"}}
simpleRecursionWriteTest: {"#id":1,"mySelf":1,"b":null,"value":"AV"}
simpleWriteTest: {"#id":1,"mySelf":null,"b":null,"value":"AV"}

MOXy. generate JSON, doesn't contain reference class

I used Eclipselink MOXy to convert my POJO(using JPA) to json. and it's work.
but i have one problem. I have pojo class MAccount contain many to one relation to class MProduct,. when I convert to json, result show that class MAccount not in class MProduct.
here my class MAccount implementation:
#XmlRootElement
#Entity
#Table(name="m_account")
public class MAccount extends BaseObject implements Serializable {
private static final long serialVersionUID = UUID.randomUUID().getMostSignificantBits();
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#XmlID
private Long id;
#Column(name="account_id")
private String accountId;
#Column(name="card_number")
private String cardNumber;
//bi-directional many-to-one association to Product
#ManyToOne
#JoinColumn(name="m_product_id")
#XmlIDREF
private MProduct mProduct;
public MCustomerAccount() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getAccountId() {
return this.accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public MProduct getMProduct() {
return this.mProduct;
}
public void setMProduct(MProduct mProduct) {
this.mProduct = mProduct;
}
// Imlement base object method
...
}
here my class MProduct implementation:
#XmlRootElement
#Entity
#Table(name="m_product")
public class MProduct extends BaseObject implements Serializable {
private static final long serialVersionUID = UUID.randomUUID().getMostSignificantBits();
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#XmlID
private Long id;
#Column(name="product_code")
private String productCode;
#Column(name="product_name")
private String productName;
//bi-directional many-to-one association to MAccount
#OneToMany(mappedBy="mProduct")
#XmlInverseReference(mappedBy="mProduct")
private Set<MAccount> mAccountList;
public MProduct() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getProductCode() {
return this.productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Set<MAccount> getMAccountList() {
return this.mAccountList;
}
public void setMAccountList(Set<MAccount> mAccountList) {
this.mAccountList = mAccountList;
}
// Imlement base object method
...
}
And generate JSON from MAccount class
{"MAccount":[
{"#type":"mAccount","id":"6","accountId":"05866039901"},
{"#type":"mAccount","id":"7","accountId":"25600036290"}]
}
there is no MProduct in there, the correct json result should be like below
{"MAccount":[
{"#type":"mAccount","id":6,"accountId":"05866039901","MProduct":{"#type":"mProduct","productCode":"T01","productName":"Book"}},
{"#type":"mAccount","id":7,"accountId":"25600036290","MProduct":{"#type":"mProduct","productCode":"T02","productName":"Pen"}}]
}
Is Anyone know how to solve this problem
Thank's b4
Because you are annotating the field, there is a chance that JPA has not populated that field yet due to lazy loading. If you annotate the property (get/set) instead do you still see this behaviour?
For more information on #XmlInverseReference see:
http://bdoughan.blogspot.com/2010/07/jpa-entities-to-xml-bidirectional.html

Resources