6

I have a class that looks something like this:

@Entity
public class EdgeInnovation {
    @Id
    public long id;
    @ManyToOne
    public NodeInnovation destination;
    @ManyToOne
    public NodeInnovation origin;
}

and another one that looks something like this:

@Entity
public class NodeInnovation {
    @Id
    public long id;
    @OneToOne
    public EdgeInnovation replacedEdge;
}

and so each table map to the other, so one entity will refer to other entities that will refer to more entities and so on, so that in the end there will be many entities that will be fetched from the database. Is there any way to only get the value (integer/long) of the key and not the entity it refers to? something like this:

@ManyToOne(referToThisTable="NodeInnovation")
@Entity
public class EdgeInnovation {
    @Id
    public long id;
    @ManyToOne(referToTable="NodeInnovation")
    public Long destination;
    @ManyToOne(referToTable="NodeInnovation")
    public Long origin;
}

and

@Entity
public class NodeInnovation {
    @Id
    public long id;
    @OneToOne(referToTable="EdgeInnovation")
    public Long replacedEdge;
}

Here's an example.

Here's an example. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.

Darwin von Corax
  • 5,201
  • 3
  • 17
  • 28
jørgen k. s.
  • 135
  • 1
  • 2
  • 8

4 Answers4

7

You would just map the foreign keys as basic mappings instead of Relationships:

@Entity
public class EdgeInnovation {
    @Id
    public long id;
    @Column(name="DESTINATION_ID")
    public Long destination;
    @Column(name="ORIGIN_ID")
    public Long origin;
}

Or you can have access to both the ID and the referenced entity within EdgeInnovation, but you'll need to decide which you want to use to set the mapping:

@Entity
public class EdgeInnovation {
    @Id
    public long id;
    @Column(name="DESTINATION_ID", updatable=false, insertable=false)
    public Long destination_id;
    @ManyToOne
    public NodeInnovation destination;
    @Column(name="ORIGIN_ID", updatable=false, insertable=false)
    public Long origin_id;
    @ManyToOne
    public NodeInnovation origin;
}

In the above example, the origin_id is read-only while the origin reference is used to set the foreign key in the table. Any changes though should be made to both fields to keep the object mappings in synch with each other.

Another alternative is to use the provider's native code to find if the reference is lazy and wasn't triggered, and then get the foreign key value. If it has been triggered, you can just use the reference to get the ID value, since it won't cause a query to fetch anything. This is something you would have to look into EclipseLink's source code for though.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • I couldn't get your second option to work, so I just ended up using the first. It also solved a problem I had with updating entities. – jørgen k. s. Mar 28 '16 at 22:50
2

Sorry, I cant comment so I put it here , I think it should be like that

@Entity
public class EdgeInnovation {
    @Id
    public long id;
    @ManyToOne
    public NodeInnovation destination;
    @ManyToOne
    public NodeInnovation origin;
}

And the other class is :

 @Entity
    public class NodeInnovation {
        @Id
        public long id;
        @OneToMany(mappedBy="origin")
        public List<EdgeInnovation> replacedEdges;
    }

If I'm getting the situation wrong sorry, (Could you draw your classes with the relations so I can get it straight?)

SeleM
  • 9,310
  • 5
  • 32
  • 51
  • No, the field "replacedEdge" in NodeInnovation has nothing to do with the fields "destination" and "origin" in EdgeInnovation. When a new row is inserted into NodeInnovation "replacesEdge" will refer to an older EdgeInnovation, and when a new row is inserted into EdgeInnovation "origin" and "destination" will both be refering to older NodeInnovation rows. – jørgen k. s. Mar 25 '16 at 01:27
  • [ER-diagram, probably not 100% correct on the signs at the end of the connections when it comes to optional vs mandatory](https://drive.google.com/file/d/0B3jy9wMk6GxmM0huTmJYUmhkMHc/view?usp=sharing) eta: the two from origin and destination to id are the wrong way around. – jørgen k. s. Mar 25 '16 at 01:41
  • I have edited the question to make more it more clear. sorry for the messy formulation and diagram. I'll make the excuse that it is currently 3 am, so I will be back tomorrow. – jørgen k. s. Mar 25 '16 at 02:11
0

Why not use a new construction in JPA and a custom constructor in NodeInnovation? Basically, create a transient property in NodeInnovation for use when you only want the EdgeInnovation id:

@Entity
public class NodeInnovation {
    @Id @GeneratedValue private Long id;
    private Integer type;
    @OneToOne
    private EdgeInnovation replacedEdge;
    @Transient
    private Long replacedEdgeId;
    public NodeInnovation() {}
    public NodeInnovation(Long id, Integer type, Long replacedEdgeId ) {
        this.id = id;
        this.type = type;
        this.replacedEdgeId = replacedEdgeId;
    }
    ...
}

Use it like so:

NodeInnovation n = em.createQuery("select new NodeInnovation(n.id, n.type, n.replacedEdge.id) from NodeInnovation n where n.id = 20", NodeInnovation.class).getSingleResult();

You didn't say how you were selecting NodeInnovation, whether directly or through a join, but either way the trick is the new NodeInnovation in the JPQL or CriteriaBuilder query.

K.Nicholas
  • 10,956
  • 4
  • 46
  • 66
  • This looks like what I want, I found out that if I added "fetch = FetchType.LAZY" to the keys they would not load "the next level" until I accessed the object witch is not ideal but does the job. And then I moved on to other problems, but I will be back today or tomorrow to tell you if it worked and to mark your answer as correct if it did. – jørgen k. s. Mar 27 '16 at 01:41
  • Sounds good. You could also store the `replacedEdgeId` in the `replacedEdge` property by doing a `new EdgeInnovation()` and then setting the id, this being done in this custom constructor. This would have the advantage be being able to use this `NodeInnovation` in subsequent queries. – K.Nicholas Mar 27 '16 at 01:57
0

I am aware I am quite late but some people might look for an answer to the same question - in your JPA repository you could do something like this:

@Query("SELECT new java.lang.Integer(model.id) FROM #{#entityName} model WHERE model.relationModeFieldlName.id IN :relationModelIds")
List<Integer> findIdByRelationModelIdIn(@Param("relationModelIds") List<Long> relationModelIds);
Phoenix
  • 15
  • 7