Error when trying to map a recursive relation - java

I'm trying to map a recursive relation using JPA. I don't know why I'm getting some errors on the #OneToMany line.
Here is a code sample:
#Entity
public class RecursiveType implements Serializable {
private static final long serialVersionUID = -2459343636539882731L;
#Id
public int id;
#OneToMany(mappedBy="rec1", cascade=CascadeType.PERSIST) //here is where I get the errors
public RecursiveType rec1;
public Map<String, Map<RecursiveType, List<Map<RecursiveType, List<InnerTypes>>>>> rec2;
//getters and setters
}
And the line of persistence.xml file
<class>pt.ptinovacao.persistencetester.model.RecursiveType</class>
I've had several errors like: "Target entity is not defined" and "The attribute type for a collection mapping must be java.util.Collection…"
Why does this happen?

I've fixed it, I just changed the annotations and the relation type
#Entity
public class RecursiveType implements Serializable {
private static final long serialVersionUID = -2459343636539882731L;
#Id
public int id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="REC1_ID", referencedColumnName="ID")
public RecursiveType rec1;
public Map<String, Map<RecursiveType, List<Map<RecursiveType, List<InnerTypes>>>>> rec2; //I really don't need to try and map this xD
//getters and setters
}

Related

Hibernate: How to map a class that "has-a" subclass to a single table?

I have a class BacktestResult that contains a property BacktestInputs. I want hibernate to create a single table that contains the contents of both. Here's what I have so far:
#Entity
#Table(name="backtest_result")
public class BacktestResult {
#Id
Long backtest_id;
public int userId;
BacktestInputs inputs;
#OneToMany
#OrderColumn(name="backtest_id")
public BacktestChartDataPoint[] series;
//getters and setters
The BacktestChartDataPoint class looks like this:
#Entity
#Table(name="backtestchart_datapoint")
public class BacktestChartDataPoint {
#Id
public Long id;
public Long backtest_id;
#ManyToOne
#JoinColumn(name="backtest_id", nullable=false, insertable=false, updatable=false)
private BacktestResult backtestResult;
The BacktestInputs class looks like this:
public class BacktestInputs {
public String strategy_name;
public String version;
etc...
As long as I comment out the BacktestInputs in BacktestResult, hibernate correctly creates 3 tables:
backtest_result
backtest_result_series
backtestchart_datapoint
But I want the contents of BacktestInputs to be included in the BacktestResult table. If I uncomment it, get error:
Could not determine type for:
net.tekknow.moneymachine.model.BacktestInputs, at table:
backtest_result, for columns: [org.hibernate.mapping.Column(inputs)]
What is the proper way to handle class hierarchy with "has-a" relationships, NOT "is-a" relationships?
You need to annotate the class BacktestInputs with #Embeddable
(see https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#embeddables)

JPA #Id on #OneToOne Entity with a Composite Key

I have an #Entity class, with an #Id annotation and a #OneToOne annotation on the same field. Usually this would not be a problem, but the entity class of the field with these annotations uses a composite key. This is causing more complications than I anticipated.
Here is the entity class that is posing the problem:
#Entity
public class ReportDetails implements Serializable {
#Id
#OneToOne
private MachineLine machineLine;
}
And here is the MachineLine entity class that is being used as an ID in ReportDetails:
#Entity
#IdClass(MachineLine.MachineLineKey.class)
public class MachineLine {
#Id
#ManyToOne
private Machine machine;
#Id
private long lineIndex;
public static class MachineLineKey implements Serializable {
private Machine machine;
private long lineIndex;
}
}
I have left out any extra fields and the getters and setters from these class definitions, to save space.
When I try to run my application it gives the following exception:
java.lang.IllegalArgumentException: This class [class ReportDetails] does not define an IdClass
When I put an #IdClass annotation on ReportDetails it then requires defining the individual fields of whatever class I define in #IdClass, like in MachineLine. However, I am trying to avoid doing this, in favour of having the whole MachineLine entity returned whenever a ReportDetails entity is retrieved from the database.
Is there a way of having MachineLine as the ID field of ReportDetails, without having to define extra fields within ReportDetails?
This is what JPA calls a "derived identity". You might try something like this:
ReportDetails:
#Entity
public class ReportDetails implements Serializable {
// all attributes map by the relationship: AttributeOverride is not allowed
#EmbeddedId
private MachineLine.Id id;
#MapsId
#JoinColumns({
#JoinColumn(name="machineId", referencedColumnName="machineId"),
#JoinColumn(name="machineLineIndex", referencedColumnName="index")
})
#OneToOne
private MachineLine machineLine;
// ...
}
MachineLine:
#Entity
public class MachineLine {
#EmbeddedId
private Id id;
#MapsId("machineId") // maps machineId attribute of embedded id
#ManyToOne
private Machine machine;
// ...
#Embeddable
public static class Id implements Serializable {
private long machineId; // corresponds to PK type of Machine
private long index;
// ...
}
}
Machine:
#Entity
public class Machine {
#Id
private long id;
#OneToMany(mappedBy = "machine")
private List<MachineLine> lines;
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

More problems with JPA deletion of row in database

I have the following code that defines the relationship between three tables.
public class Attachment implements Serializable {
#Id
#Column(name="attachment_id")
private int attachmentId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
#OneToMany(mappedBy="attachment")
private List<Reference> references;
MORE STUFF;
}
public class Uuid implements Serializable {
#Id
#Column("name=uuid_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int uuidId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
MORE STUFF
}
public class Reference implements Serializable {
#Id
#Column(name="reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceId;
#OneToMany(mappedBy="reference")
private List<Attachment> attachments;
#ManyToOne(cascade=CascadeType.MERGE)
#JoinColumn(name="attachment_id")
private Attachment attachment;
#OneToMany(mappedBy="reference")
private List<Uuid> uuids;
MORE STUFF
}
I have some more code that picks the specific "uuid" object/row that needs to be deleted, and the idea is that anything in the other tables that needs deleting because they share the same reference_id should be deleted too. The code that does this is:
try {
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.remove(data);
transaction.commit();
} catch (final PersistenceException e) {
throw new CPDPersistenceException(e);
}
When the delete is performed it throws Exception "Cannot delete or update parent row: a foreign key constraint fails. I posted something on a variation of this before. Does anybody have any ideas? Thanks for your time.
You have one one-to-many relationship between Reference and Attachment defined but also a many-to-one between Reference and Attachment (and vice versus), this will never work. You should have either one of them, not both, or a many-to-many relationship.
Fix that and it should be much easier to delete objects.
For instance:
public class Attachment implements Serializable {
#Id
#Column(name="attachment_id")
private int attachmentId;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="reference_id")
private Reference reference;
//MORE STUFF;
}
public class Reference implements Serializable {
#Id
#Column(name="reference_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int referenceId;
#OneToMany(mappedBy="reference")
private List<Attachment> attachments;
#OneToMany(mappedBy="reference")
private List<Uuid> uuids;
/bMORE STUFF
}

How do I retrieve only the ID instead of the Entity of an association?

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. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.
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.
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?)
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.
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);

Spring JPA repository doesn't return List of entities

I try to get simple List of Rawtype entities with help of findBy method in the myMethod. But I get nothing - rawtypes doesn't contain any entity. Although findAll method works fine. Please tell we where is my mistake.
Rawtype.java
#Entity
#Table(name="rawtype")
public class Rawtype implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="rtid", nullable = false)
#GeneratedValue
private int rtId;
#Column(name="rtname", nullable = false)
private String rtName;
//getters and setters
RawtypeRepository.java
public interface RawtypeRepository extends JpaRepository<Rawtype, Integer> {
List<Rawtype> findByRtName(String rtName);
}
RawtypeServiceImpl.java
#Service
#Transactional
public class RawtypeServiceImpl implements RawtypeService {
#Autowired
RawtypeRepository rawtypeRepository;
public List<Rawtype> findAll() {
return rawtypeRepository.findAll();
}
public myMethod(){
List<Rawtype> rawtypes = rawtypeRepository.findByRtName("RawName");
}
}
Can you try printing rtName of all the entities returned by findAll() method? May be there isn't any record with 'RawName' as rtName.
Also, you can enable logging for JPA to see the generated query.

Categories