Spring data neo4j - polymorphic children collection - java

I have following classes:
public interface Label {
String getValue();
}
#Node
#Data
public class SimpleLabel implements Label {
#Id
#GeneratedValue
private Long id;
private String value;
#Relationship(value = "JE")
private Set<SimpleLabel> labels = new HashSet<>();
public void addLabel(SimpleLabel label){
labels.add(label);
}
}
#Node
#Data
public class HyperLabel implements Label {
#Id
#GeneratedValue
private Long id;
#Relationship(value = "JE")
private Set<Label> labels = new HashSet<>();
public void addLabel(Label label){
labels.add(label);
}
#Override
public String getValue() {
return labels.stream().map(Label::getValue).collect(Collectors.joining("; "));
}
}
and this is the query I'm using:
#Query("MATCH (h:HyperLabel)-[r:JE]-(s:SimpleLabel) WHERE ID(h)=$id RETURN h,COLLECT(r),COLLECT(s)")
Optional<HyperLabel> getById(Long id);
The problem, I'm having is that labels collection that is returned by query is empty. But if I make a slight change in the code of HyperLabel class (change Set<Label> to Set<SimpleLabel>), then it works.
Is there any way to make it work with Label interface, since children of HyperLabel can be both HyperLabel and SimpleLabel instances?

Related

Null columns are created on either tables when accessing data using Spring Data JPA

I am new to Spring Data JPA and Hibernate. By looking at different examples I built a working model for CRUD operations on one entity, I am having trouble in joining two tables to extract AF_NAME using AF_ID from another table which is Foreign key. A null column is created with the names of and while accessing, null is returned.please check if I am following preocedure for joins and point me to any tutorial know.
I followed this solution and still there is no progress.
#Entity
#Configuration
#EnableAutoConfiguration
#Table(name = "AFF_CONFIG")
public class AFF_CONFIG implements Serializable {
#Id
#Column(name = "AFF_CONFIG_ID")
private String AFF_CONFIG_ID;
#Column(name = "AFF_ID")
private String AFF_ID;
#Column(name = "CH_ID")
private String CH_ID;
#Column(name = "M_ID")
private Long M_ID;
#Column(name = "KEY")
private String KEY;
#Column(name = "VALUE")
private String VALUE;
#Column(name = "SYSTEM")
private String SYSTEM;
private AFF aff;
#LazyCollection(LazyCollectionOption.TRUE)
#ManyToOne
#JoinColumn(name = "AFF_ID")
public AFF getAff() {
return aff;
}
public void setAffiliate(AFF aff) {
this.aff = aff;
}
public String getAFF_CONFIG_ID() {
return AFF_CONFIG_ID;
}
public void setAFF_CONFIG_ID(String aFF_CONFIG_ID) {
AFF_CONFIG_ID = aFF_CONFIG_ID;
}
public String getAFF_ID() {
return AFF_ID;
}
public void setAFF_ID(String aFF_ID) {
AFF_ID = AFF_ID;
}
public String getCH_ID() {
return CH_ID;
}
public void setCHANNEL_ID(String cH_ID) {
CH_ID = cH_ID;
}
public Long getM_ID() {
return M_ID;
}
public void setM_ID(Long m_ID) {
M_ID = m_ID;
}
public String getKEY() {
return KEY;
}
public void setKEY(String kEY) {
KEY = kEY;
}
public String getVALUE() {
return VALUE;
}
public void setVALUE(String vALUE) {
VALUE = vALUE;
}
public String getSYSTEM() {
return SYSTEM;
}
public void setSYSTEM(String sYSTEM) {
SYSTEM = sYSTEM;
}
Second entity is:
#Entity
#Table(name = "AFF")
public class AFF implements Serializable {
#Column(name = "AFF_NAME")
private String AFF_NAME;
#Column(name = "AFF_CODE")
private String AFF_CODE;
#Id
#Column(name = "AFF_ID")
private String AFF_ID;
private Set<AFF_CONFIG> someAff = new HashSet<AFF_CONFIG>();
#LazyCollection(LazyCollectionOption.TRUE)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "aff")
public Set<AFF_CONFIG> getSomeAff() {
return someAff;
}
public void setSomeAff(Set<AFF_CONFIG> someAff) {
this.someAff = someAff;
}
public String getAFF_ID() {
return AFF_ID;
}
public void setAFF_ID(String aFF_ID) {
AFF_ID = aFF_ID;
}
public String getAFF_NAME() {
return AFF_NAME;
}
public void setAFF_NAME(String aFF_NAME) {
AFF_NAME = aFF_NAME;
}
public String getAFF_CODE() {
return AFF_CODE;
}
public void setAFF_CODE(String aFF_CODE) {
AFF_CODE = aFF_CODE;
}
Since this is many to one relation I created set type in one and object type in another as defined in other places.Created a repository by extending crud and added a query. Excise the bunch of different annotations, I included them in hoping to solve the null entry.
#Repository
public interface MarketRepository extends CrudRepository<AFF_CONFIG,String> {
Page<AFF_CONFIG> findAll(Pageable pageable);
#Query("Select a,b from AFF_CONFIG a, AFF b where a.AFF_ID = b.AFF_ID" )
public List<AFF_CONFIG> getAffData();
}
the applicatoin is working fine even after some tinkering until I Included these annotations. Now there is this error:
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: aff.
I solved the issue with the help of my Supervisor. Looks like we have to follow naming specifications for Class and variables. And one more correction is to remove collection type object and change it to just object (removed set in aff class).I will post the corrected later, to compare and contrast.

Ebean EmbeddedId mapping column to ManyToOne relation

I'm facing a problem with Ebean when trying to create Composed Keys (EmbeddedId).
This is a draft of what I have:
#Entity public class EntityA extends Model{
#Id
private String ID;
#OneToMany
private List<EntityB> listEntitesB;
public EntityA(){
ID = UUID.randomUUID();
}
}
#Entity public class EntityB extends Model{
#EmbeddedId
private EntityB_PK ID;
#ManyToOne
#JoinColumn(name="entityA_fk", referencedColumnName="listEntitiesB")
private EntityA entityA;
public EntityB(String entityB_ID){
ID = new EntityB_PK(UUID.randomUUID(), entityB_ID);
}
}
#Embeddable public class EntityB_PK{
private String entityB_ID;
private String entityA_FK_ID;
public EntityB_PK(String entB_ID, String entA_FK_ID){
entityB_ID = entB_ID;
entityA_FK_ID = entA_FK_ID;
}
}
Note: I'm using Ebean 4.1.4 with Java 1.6.
So, this code works well, but there is a perk, which I'm trying to solve - the resultant table in the database looks like this:
entityB_ID [primary]
entityA_FK_ID [primary]
entityA_fk
As you can see that last column is redundant considering the "entityA_FK_ID" column.
What I would like to have?
I would like to be able to tell Ebean to use the column "entityA_FK_ID" for the association #ManyToOne instead of creating it's own column.
To solve this problem we have to:
Map EntityB.entityA to the same column as EntityB.ID.entityA_FK_ID
set 'insertable' and 'updateable' attributes of EntityB.entityA #JoinColumn annotation
Override setter of EntityB.entityA
Here is the code:
EntityA.java:
#Entity
public class EntityA extends Model {
#Id
private String ID;
#OneToMany(mappedBy="entityA")
public List<EntityB> listEntitesB;
public static Finder<String,EntityA> find = new Finder<String,EntityA>(
String.class, EntityA.class
);
public EntityA() {
ID = UUID.randomUUID().toString();
}
public String getID() {
return ID;
}
}
EntityB.java:
#Entity
public class EntityB extends Model {
#EmbeddedId
private EntityB_PK ID;
#ManyToOne
#JoinColumn(name = "entityA_fk_id", insertable = false, updatable = false)
private EntityA entityA;
public EntityA getEntityA() {
return entityA;
}
public void setEntityA(EntityA aEntityA) {
entityA = aEntityA;
ID.entityA_FK_ID = aEntityA.getID();
}
public EntityB(String entityB_ID){
ID = new EntityB_PK(UUID.randomUUID().toString(), entityB_ID);
}
public String getID() {
return ID.entityB_ID;
}
}
EntityB_PK.java:
#Embeddable
public class EntityB_PK implements Serializable {
public String entityB_ID;
#Column(name="entityA_fk_id")
public String entityA_FK_ID;
public EntityB_PK(String entB_ID, String entA_FK_ID){
entityB_ID = entB_ID;
entityA_FK_ID = entA_FK_ID;
}
#Override
public int hashCode() {
return entityB_ID.length() + entityA_FK_ID.length();
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
EntityB_PK b = (EntityB_PK)obj;
if(b==null)
return false;
if (b.entityB_ID.equals(entityB_ID) && b.entityA_FK_ID.equals(entityA_FK_ID)) {
return true;
}
return false;
}
}

Spring MVC CrudRepository findByIn

I have a CrudRepository that is supposed to make a query with an array (findByIn). In my repository tests it works, but when I try to use the query in my service, it doesn't work. Could someone explain why it doesn't work? Here is my setup (excluding some code irrelevant to the question)
Database model:
#Entity
#Table(name="Place")
public class Place implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "placeId", nullable = false)
private Long placeId;
#Column(name = "owner", nullable = false)
private String owner;
public Long getPlaceId() {
return placeId;
}
public void setPlaceId(Long placeId) {
this.placeId = placeId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
Repository:
#Repository
public interface PlaceRepository extends CrudRepository<Place, Long> {
List<Place> findByPlaceIdIn(Long[] placeId);
}
Service (this is the part not working):
#Service
public class PlaceService {
#Autowired
private PlaceRepository placeRepository;
public List<Place> getPlaces(Long[] placeIds) {
return placeRepository.findByPlaceIdIn(placeIds);
}
}
The problem is that in my service placeRepository.findByPlaceIdIn(placeIds) returns 0 objects if placeIds contains more than one item. If placeIds contains just one item, the query works fine. I tried replacing return placeRepository.findByPlaceIdIn(placeIds) with this piece of code that does the query for every array item one by one (this actually works, but I'd like to get the query work as it should):
ArrayList<Place> places = new ArrayList<Place>();
for (Long placeId : placeIds) {
Long[] id = {placeId};
places.addAll(placeRepository.findByPlaceIdIn(id));
}
return places;
I know that the repository should work, because I have a working test for it:
public class PlaceRepositoryTest {
#Autowired
private PlaceRepository repository;
private static Place place;
private static Place place2;
private static Place otherUsersPlace;
#Test
public void testPlacesfindByPlaceIdIn() {
place = new Place();
place.setOwner(USER_ID);
place2 = new Place();
place2.setOwner(USER_ID);
place = repository.save(place);
place2 = repository.save(place2);
Long[] ids = {place.getPlaceId(), place2.getPlaceId()};
assertEquals(repository.findByPlaceIdIn(ids).size(), 2);
}
}
I also have another repository for other model, which also uses findByIn and it works fine. I can't see any relevant difference between the repositories. I thought it might offer some more details to show the working repository, so I included it below:
Database model:
#Entity
#Table(name="LocalDatabaseRow")
#JsonIgnoreProperties(ignoreUnknown=false)
public class LocalDatabaseRow implements Serializable {
public LocalDatabaseRow() {}
public LocalDatabaseRow(RowType rowType) {
this.rowType = rowType;
}
public enum RowType {
TYPE1,
TYPE2
};
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#JsonProperty("id")
private Long id;
#JsonProperty("rowType")
#Column(name = "rowType")
private RowType rowType;
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public RowType getRowType() {
return rowType;
}
public void setRowType(RowType rowType) {
this.rowType = rowType;
}
}
Repository:
#Repository
public interface LocalDatabaseRowRepository extends CrudRepository<LocalDatabaseRow, Long> {
List<LocalDatabaseRow> findByRowTypeAndUserIdIn(RowType type, String[] userId);
}
try using a list instead :
findByPlaceIdIn(List placeIdList);
You have a typo in your code (the repository declaration in the service):
#Autowired
private placeRepository placeRepository;
Should be:
#Autowired
private PlaceRepository placeRepository;

Play Framework: No #javax.persistence.Id field found in class

I have this Play Model class that I'm trying to modify an object of, and when I want to save it, I get the following exception:
java.lang.RuntimeException: No #javax.persistence.Id field found in class [class models.Contact]
at play.db.ebean.Model._idAccessors(Model.java:39)
at play.db.ebean.Model._getId(Model.java:52)
The class:
#Entity
public class Contact extends Model implements Person {//, Comparable<Contact>{
private Long id;
private Client client;
#Required
private String email;
private String profil_picture;
private Boolean active = new Boolean(true);
private Boolean favorite = new Boolean(false);
#Transient
private Boolean profile_pic_url_init = new Boolean(false);
#Id
#GeneratedValue
public Long getId() {
return id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="client_id")
public Client getClient(){
return client;
}
public void setClient(Client client){
this.client= client;
}
#Column
public Boolean getFavorite() {
return favorite;
}
public void setFavorite(Boolean is_favorite) {
this.favorite = is_favorite;
}
....
}
The code calling the save() method:
List<Contact> contacts_list = current_client.getContacts();
for (Contact c : contacts_list) {
c.setFavorite(false);
c.save();
}
The class actually has an #Id annotation, so any guesses of why this doesn't work? I tried looking it up on google, but couldn't find much about this error. Thanks in advance!
Move #Id annotation to id field instead of its getter.

Hibernate JPA: mappedBy reference exception

I want to map the following classes with Hibernate JPA:
My code looks like this:
#Entity
public class Grid{
#Id
#GeneratedValue
private Long id;
#Column(unique=true)
private String name;
private String location;
private BigDecimal costsPerCPUMinute;
#OneToMany(mappedBy="grid")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#Entity
public class User extends Person{
#Column(nullable=false, unique=true)
private String username;
#Column(length=16,columnDefinition="BINARY(16)")
private byte[] password;
#OneToMany(mappedBy="user")
private List<Membership> mem;
public List<Membership> getMem() {
return mem;
}
public void setMem(List<Membership> mem) {
this.mem = mem;
}
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#Id
#ManyToOne
private Grid grid;
#Id
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Unfortunately, I get the following Exception:
Caused by: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: dst1.model.Membership.grid in dst1.model.Grid.mem
As far as I understand the message, grid cannot be found in Membership. But as you can see in de code, there definitly is a variable named grid in the Membership class.
Does anybody has an idea whats going wrong?
Update: As suggested in the comments, I also tried to change the Membership Class by using IDClass or EmbeddedID. The EmbeddedID version looks like this:
#SuppressWarnings("serial")
#Entity
public class Membership implements Serializable{
private Date registration;
private Double discount;
#EmbeddedId
private MembershipPK membershipPK;
public Membership(){};
public MembershipPK getMembershipPK() {
return membershipPK;
}
public void setMembershipPK(MembershipPK membershipPK) {
this.membershipPK = membershipPK;
}
#SuppressWarnings("serial")
#Embeddable
public class MembershipPK implements Serializable{
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
public Grid getGrid() {
return grid;
}
public void setGrid(Grid grid) {
this.grid = grid;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Unfortunately, I still get the same exception.
Update 2: I will rewrite all three classes from scratch tomorrow evening and update this post if that changes anything.
You should be able to use something like
#Embeddable
public class MembershipId
{
protected Grid grid;
protected User user;
}
#Entity
public class Membership {
#EmbeddedId
MembershipId id;
}
#Entity
public class User {
#OneToMany(mappedBy="id.user")
private Set<Membership> memberships = new HashSet<Membership>();
}
From the top of my head: shouldn't this be rather
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
#IdClass(MembershipId.class)
public class Membership implements Serializable {
#Id #ManyToOne
private Grid grid;
#Id #ManyToOne
private User user;
// rest of class
}
Edit: What the exception is telling you is that your Grid class has a field named mem and that the entity represented by this field needs a grid field, but doesn't have one. Here is where your Grid.mem needs a grid field:
#Entity
public class Grid{
...
#OneToMany(mappedBy="grid")
private List<Membership> mem;
This mapping can only work if there is a property grid in Membership.class. If you hide the grid inside the IdClass, Grid.mem can't find it. You might try this:
#Embeddable
public class MembershipId implements Serializable {
private Grid grid;
private User user;
// immutable constructor, getters, equals, and hashCode
}
#Entity
public class Membership implements Serializable {
#EmbeddedId
private MembershipId id;
#ManyToOne
private Grid grid;
#ManyToOne
private User user;
// rest of class
}

Categories

Resources