the problems is when #ManyToOne make a #Joincolumn ID_REPORT (it´s a primary key ) and #Joincolumn ID_TEMPLATE_DEFAULT
Repeated column in mapping for entity: CurReport column: id_report (should be mapped with insert="false" update="false")
Code
First table CUR_TEMPLATE
CREATE TABLE CUR_TEMPLATE
(
ID_REPORT NUMBER(5,0) NOT NULL,
ID_TEMPLATE NUMBER(5,0) NOT NULL,
-- Other fields
);
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT PK_CUR_TEMPLATE PRIMARY KEY (ID_REPORT, ID_TEMPLATE)
-- CUR_TEMPLATE foreign keys
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT FK_CUR_PLAN_REFERENCE_CUR_REPO FOREIGN KEY (ID_REPORT)
REFERENCES CUR_REPORTS (ID_REPORT);
Second table CUR_REPORTS
-- CUR_REPORTS definition
CREATE TABLE CUR_REPORTS
(
ID_REPORT NUMBER(3,0) NOT NULL,
NAME_REPORT VARCHAR2(100) NOT NULL,
-- other fields
ID_TEMPLATE_DEFAULT NUMBER(5,0),
-- other fields
) ;
ALTER TABLE CUR_REPORTS ADD CONSTRAINT PK_CUR_REPORTS PRIMARY KEY (ID_REPORT)
ALTER TABLE CUR_REPORTS CONSTRAINT FK_CUR_REPO_REFERENCE_CUR_PLAN FOREIGN KEY (ID_REPORT, ID_TEMPLATE_DEFAULT)
REFERENCES CUR_TEMPLATE (ID_REPORT, ID_TEMPLATE)
First table CUR_REPORTS Entity CurReport
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#ManyToOne(fetch = FetchType.LAZY) <---WHERE IS THE PROBLEM
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORTE")
#JoinColumn(name = "ID_TEMPLATE_DEFAULT", referencedColumnName = "ID_TEMPLATE")
private CurTemplate curTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
}
Second table CUR_TEMPLATE Entity CurReport
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_REPORT", nullable = false)
private Long idReport;
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
When i add insertable=false, updatable=false
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORT", insertable=false, updatable=false)
said
Mixing insertable and non insertable columns in a property is not allowed: CurTemplate
How could i map those relationships?
How resolve the #JoinColumn when one field of the FK are column PK?
You can use a derived identity and map CurTemplate like this:
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurTemplatePK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#Id
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
Then you will need an #IdClass like this:
public class CurTemplatePK {
Long idTemplate; // matches name of #Id attribute
Long curReport; // matches name of #Id attribute and type of CurReport PK
}
Then you should use a basic mapping for the default template key and provide a getter for the default template object:
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#Column(name = "ID_TEMPLATE_DEFAULT")
private Long idDefaultTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
public CurTemplate getDefaultTemplate() {
return this.curTemplates.stream()
.filter(template -> template.getIdTemplate().equals(idDefaultTemplate))
.findFirst()
.orElse(null);
{
}
If you want to allow clients to set the default template, you will need to implement a setter that first verifies that the new default template is already in the set curTemplates.
Related
I want to join a single column from another table to one of my #Entity classes:
Currently it works as follows:
#Entity
public class Product {
#Id
private long id; //autogenerated
String type; //used for mapping
#ManyToOne
#JoinColumn(name = "type", insertable = false, updatable = false)
ProductMapping mapping;
}
#Entity
public class ProductMapping {
#Id
String type;
String longname;
}
Question: how could I replace the #ManyToOne mapping to directly map to String longname?
//TODO: how to directly map to 'mapping.longname'?
#JoinColumn(name = "type", insertable = false, updatable = false)
String mapping.longname;
You can use #Formula annotation with a query like this :
#Formula("(select pm.longname from product_mapping pm where pm.COL_NAME = value)")
private String longName;
//give the column name of type from product_mapping table
OR
You can also use the below approach :
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "type", referencedColumnName = "COL_NAME", insertable = false, updatable = false)
ProductMapping mapping;
The other entity use #NaturalId annotation on the field.
#Entity
public class ProductMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
String type;
#NaturalId
#Column(name = "SOME_VALUE")
String longname;
}
I have two entities Space as parent and SpaceInactivityDate as child with relation #OneToMany and #ManyToMany and with space_id as a foreign key.
The insert operation works fine but when I tried to update, the Space table was updated but in the SpaceInactivityDate table instead of updating rows he added new lines. I have used the CascadeType=All and cascade = {CascadeType.PERSIST,CascadeType.MERGE} but usually I had the same result.
Space.java
#Entity
#Table(name = "space")
public class Space {
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, mappedBy = "space", fetch = FetchType.LAZY)
private Set<SpaceInactivityDate> spaceInactivityDates = new HashSet<SpaceInactivityDate>();
// getters+setters
}
SpaceInactivityDate .java
#Table(name="space_incativity_date")
#Entity
public class SpaceInactivityDate {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer id;
#Column(name = "inactivity_start_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityStartDate;
#Column(name = "inactivity_end_date", nullable = false, insertable = true, updatable = true)
#Type(type = "ec.ep.dit.sip.expomep.persistency.type.PersistentLocalDate")
private LocalDate inactivityEndDate;
#JoinColumn(name="space_id",referencedColumnName="id")
#ManyToOne(optional=false,fetch=FetchType.LAZY)
private Space space;
}
SpaceServiceImpl.java
this.spaceDao.save(space);
I trying to map my class to database,the class reference to itself by "id_comment_parent", but when running a test, I get the following error:
Caused by: org.hibernate.AnnotationException: A Foreign key refering com.ecommerce.entities.Comment from com.ecommerce.entities.Comment has the wrong number of column. should be 2
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:646) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:102) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1814) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
these is my classe:
#Entity
#Table(name = "comment", catalog = "Ecommerce_db")
public class Comment implements Serializable {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "idComment", column = #Column(name = "id_comment", nullable = false)),
#AttributeOverride(name = "idCommentParent", column = #Column(name = "id_comment_parent", nullable = false)) })
private CommentId id;
#Column(name = "id_user", nullable = false)
private long idUser;
#Column(name = "comment", length = 65535)
private String comment_1;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_comment_parent", nullable = false, insertable = false, updatable = false)
private Comment comment;
}
anyone have any idea?
Looks like CommentId that represents de Primary Key of Commenthas more that one column. So the ForeignKey must have more than one JoinColumn too.
Besides, it seems that you are mixing the fields of your primary key and your foreign key, as you have id_comment_parent in both primary key and foreign key.
You actually have comment entities with the same id_comment but with different id_comment_parent? It that's not the case, I think that a more coherent mapping would be that id_comment is the primary key, and id_comment_parent is the foreing key. Something like this:
#Entity
#Table(name = "comment", catalog = "Ecommerce_db")
public class Comment implements Serializable {
#Id
#Column(name = "id_comment", nullable = false)
private Long id;
#Column(name = "id_user", nullable = false)
private long idUser;
#Column(name = "comment", length = 65535)
private String comment_1;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_comment_parent")
private Comment parent;
}
I have two entities
Entity 1
#Entity
#Table(name = "Table1")
public class Table1{
#Id
#SequenceGenerator(name = "Table1_SEQ", sequenceName = "Table1_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Table1_SEQ")
#Column(name = "seq", updatable = false, unique = true, insertable = true)
private Long seq;
#Column(name = "number", updatable = false, insertable = true)
private Long number;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Table2 table2;
Entity 2
#Entity
#Table(name = "Table2")
public class Table2 {
#Id
#SequenceGenerator(name = "Table2_SEQ", sequenceName = "Table2_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Table2_SEQ")
#Column(name = "enumber")
private Long eNumber;
I have a repository which extends JpaRepository
public interface Table1Repository extends JpaRepository<Table1, Long> {
}
When I try to save using
Table1Repository rep;
Table1 table1; // Assume this is initialized correctly
rep.save(table1);
I get a Foriegn Key constraint error org.springframework.dao.DataIntegrityViolationException
Table1 has a Foreign Key association with Table2 on the column enumber but I don't want to insert anything into Table2.
How do I save into Table1?
I'm not too familiar with Jpa so please let me know if you need any clarifications on what I'm asking or if you need any more information.
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Table2 table2; // THIS IS WRONG!
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Long enumberFKTable2; // THIS IS CORRECT!
//getters and setters
Implement another java class similar to table1 called table2 with all the spring annotations. Then you do the following code:
//First, make sure that the foreign key exists!
number enumber = entityManager.getReference(Enumber.class, enumberId);
//test
if(enumber != null){
Table 1 table1 = new Table1();
table1.setEnumber(enumber);
entityManager.persist(table1);
}
I have the following model that I need to annotate using JPA:
Merchant(merchant_id, ...).
MerchantType(id1, id2, ...)
MerchantMerchantTypeAssociationTable(merchant_id, id1, id2)
I cannot figure out how to map the association table. Mapping Merchant is straitghtforward, so I will leave it outside of the mappings. The other mappings are as follows:
MerchantType:
#Entity
class MerchantType {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "e1_id", column=#Column(name="e1_id")),
#AttributeOverride(name = "another_id", column=#Column(name="another_id"))
})
MerchantTypePk id;
#ManyToOne
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id", insertable = false, nullable = false)
#MapsId("e1_id")
AnotherEntity1 e1;
#Column(name = "another_id", referencedColumnName = "another_id", insertable = false, nullable = false)
Long anotherId;
//Two other local fields irrelevant to the discussion here
public MerchantType(){
this.id = new MerchantTypePk();
}
//Getters and setters here.
}
//MerchantTypePk is a simple Embeddable class here below with two Long fields:
//e1_id and another_id
MerchantMerchantTypeAssociation:
#Entity
class MerchantMerchantTypeAssociation {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "e1_id", column = #Column(name = "e1_id")),
#AttributeOverride(name = "another_id", column = #Column(name = "another_id"))
#AttributeOverride(name = "offer_id", column = #Column(name = "merchant_id"))
})
private MerchantMerchantTypeAssociationPk id;
//******** HERE IS THE QUESTION
//******** HERE IS THE QUESTION
//******** HERE IS THE QUESTION
#ManyToOne
#JoinColumns({
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id", insertable = false, updatable = false),
#JoinColumn(name = "another_id", referencedColumnName = "another_id", insertable = false, updatable = false)
})
#MapsId("e1_id")
#MapsId("another_id")
private MerchantType merchantType;
//Similar mapping to the one above, but with only one Join Column
private Merchant merchant;
//One more local field that is irrelevant to the mapping
//but is the one that is forcing me to map a many - to - many relationship
//in this way.
}
//MerchantMerchantTypeAssociationPk as a simple embeddable
Question: How can I make a mapping for this kind of entities when the annotation '#MapsId' cannot be repeated and it does not accept more than one value?
You did not include the code for MerchantMerchantTypeAssociationPk, but I'm guessing it looks like this:
#Embeddable
public class MerchantMerchantTypeAssociationPk {
public MerchantPk merchantPK;
public MerchantTypePk merchantTypePK;
}
#MapsId is used to specify the attribute within the composite key to which the relationship attribute corresponds, not the columns. So MerchantMerchantTypeAssociation should look like this:
#Entity class MerchantMerchantTypeAssociation {
#EmbeddedId
private MerchantMerchantTypeAssociationPk id;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id",...),
#JoinColumn(name = "e2_id", referencedColumnName = "e2_id",...)
})
#MapsId("merchantTypePK") // <<< *attribute* in Embeddable
private MerchantType merchantType;
#ManyToOne
#JoinColumn(name = "m_id", referencedColumnName = "merchant_id",...)
#MapsId("merchantPK") // <<< *attribute* in Embeddable
private Merchant merchant;
}
Derived identities are discussed in the JPA 2.1 spec, section 2.4.1.