I'm trying to build a oneToMany relation between a Rent table and a RentAgreement table, the rent table has a composite key rentKey
#Setter
#Getter
#Entity
#Table(name = "Rent")
public class Rent {
#EmbeddedId
#Column(name = "rentKey")
private RentKey rentKey;
#OneToMany(mappedBy="rent", cascade = {CascadeType.REFRESH, CascadeType.REMOVE})
#JsonIgnoreProperties(value = "rent", allowSetters=true)
private Set<RentalAgreement> rentalAgreements;
...
The RentKey is
#Data
#Setter
#Getter
#Embeddable
public class RentKey implements Serializable{
private String rentStartDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "agent_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "rents", allowSetters=true)
private Agent agent;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "rents", allowSetters=true)
private Client client;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "propertyRent_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "rents", allowSetters=true)
private PropertyRent propertyRent;
...
and the RentAgreement is
#Data
#Entity
#Table(name = "RentalAgreement")
public class RentalAgreement {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="agent_id", referencedColumnName="agent_id"),
#JoinColumn(name="client_id", referencedColumnName="client_id"),
#JoinColumn(name="propertyRent_id", referencedColumnName="propertyRent_id")
})
#JsonIgnoreProperties(value = "rentalAgreements", allowSetters=true)
private Rent rent;
...
When I run the code, I get
org.hibernate.AnnotationException: referencedColumnNames(agent_id, client_id, propertyRent_id) of com.mikason.PropView.dataaccess.documentEntity.RentalAgreement.rent referencing com.mikason.PropView.dataaccess.commercialEntity.Rent not mapped to a single property
Could anyone tell me where I did wrong?
Thanks
Related
I created two simple entities for trying out the java persistence manytomany mapping. But whatever I try, the jointable won't be populated with a mapping and remains empty.
UserClass:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(targetEntity = Order.class ,fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "users_orders",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "order_id", referencedColumnName = "id")
)
#JsonIgnoreProperties(value = "orderUsers")
private Set<Order> userOrders = new HashSet<>();
}
OrderClass:
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(mappedBy = "userOrders", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "userOrders")
private Set<User> orderUsers = new HashSet<>();
}
I added Getter/Setter/Constructor via Lombok.
Create and save an user. Create an order, add the user and save it. But still the jointable remains empty.
Any ideas?
I have an issue with hibernate and postgres using spring boot.
When I run the application I see in postgres logs these errors(without any data bootstrap):
2021-06-25 13:55:31.057 UTC [81] ERROR: constraint "uk_j1xiaxeat9b5r1qs27ei0t2a6" of
relation "process_template_base_environmental_impact" does not exist
2021-06-25 13:55:31.057 UTC [81] STATEMENT: alter table
process_template_base_environmental_impact drop constraint
UK_j1xiaxeat9b5r1qs27ei0t2a6
2021-06-25 13:55:31.062 UTC [81] ERROR: constraint "uk_9568qjc94suvuskoi9o2li3er" of
relation "process_template_elementary_flow_template" does not exist
2021-06-25 13:55:31.062 UTC [81] STATEMENT: alter table
process_template_elementary_flow_template drop constraint
UK_9568qjc94suvuskoi9o2li3er
2021-06-25 13:55:31.064 UTC [81] ERROR: constraint "uk_dy1ej2j9qxeoq6ca641tpfxr5" of
relation "process_template_flow_template" does not exist
2021-06-25 13:55:31.064 UTC [81] STATEMENT: alter table
process_template_flow_template drop constraint UK_dy1ej2j9qxeoq6ca641tpfxr5
I have an entity called ProcessTemplate with ProcessTemplateId as compositeKey, and 3 cascades on ElementaryFlowTemplate, BaseEnvironmentalImpact, and FlowTemplate.
ProcessTemplate:
#Data
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "process_template")
public class ProcessTemplate {
#EmbeddedId
#EqualsAndHashCode.Include
private ProcessTemplateId id;
#ManyToOne
#JoinColumn(name = "fk_process_base", referencedColumnName = "id")
private ProcessBase processBase;
#ManyToOne
#JoinColumn(name = "fk_reference_flow_base", referencedColumnName = "id")
private FlowBase referenceProduct;
#OneToMany(cascade = CascadeType.ALL)
private Set<BaseEnvironmentalImpact> baseEnvironmentalImpacts;
#OneToMany(cascade = CascadeType.ALL)
#OrderBy("impactsContribute DESC" )
private Set<FlowTemplate> flowsTemplate;
#OneToMany(cascade = CascadeType.ALL)
#OrderBy("impactsContribute DESC" )
private Set<ElementaryFlowTemplate> elementaryFlowsTemplate;
#ManyToOne
#JoinColumn(name = "fk_source_info", referencedColumnName = "id")
private SourceInfo sourceInfo;
#Column(name = "version")
#Version
private Long version;
}
ProcessTemplateId:
#Embeddable
public class ProcessTemplateId implements Serializable {
#Column(name = "process_base_id")
private UUID processBaseId;
#Column(name = "reference_product_id")
private UUID referenceProductId;
public ProcessTemplateId() {
}
public ProcessTemplateId(UUID processBaseId, UUID referenceProductId) {
this.processBaseId = processBaseId;
this.referenceProductId = referenceProductId;
}
public UUID getProcessBaseId() {
return processBaseId;
}
public UUID getReferenceProductId() {
return referenceProductId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ProcessTemplateId)) return false;
ProcessTemplateId that = (ProcessTemplateId) o;
return Objects.equals(getProcessBaseId(), that.getProcessBaseId()) &&
Objects.equals(getReferenceProductId(), that.getReferenceProductId());
}
#Override
public int hashCode() {
return Objects.hash(getProcessBaseId(), getReferenceProductId());
}
}
AbstractFlowTemplate:
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractFlowTemplate {
#Id
#EqualsAndHashCode.Include
private String id;
#Enumerated(EnumType.STRING)
private FlowType flowType;
#ManyToOne
#JoinColumn(name = "fk_source_info", referencedColumnName = "id")
private SourceInfo sourceInfo;
#Column(name = "version")
#Version
private Long version;
}
ElementaryFlowTemplate:
#Data
#Entity
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
#Table(name = "elementary_flow_template")
public class ElementaryFlowTemplate extends AbstractFlowTemplate {
#ManyToOne
#JoinColumn(name = "fk_elementary_flow_base", referencedColumnName = "id")
private ElementaryFlowBase elementaryFlowBase;
}
FlowTemplate (linkedProcessTemplate is not the bidirectional relation to ProcessTemplate but another unidirectional relation, because each flowTemplate had a link to another ProcessTemplate):
#Data
#Entity
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
#Table(name = "flow_template")
public class FlowTemplate extends AbstractFlowTemplate {
#ManyToOne
#JoinColumn(name = "fk_flow_base", referencedColumnName = "id")
private FlowBase flowBase;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumns({#JoinColumn(name = "process_base_id", referencedColumnName =
"process_base_id"),
#JoinColumn(name = "reference_product_id", referencedColumnName =
"reference_product_id")})
private ProcessTemplate linkedProcessTemplate;
}
BaseEnvironmentalImpact:
#Data
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "base_environmental_impact")
public class BaseEnvironmentalImpact {
#Id
#EqualsAndHashCode.Include
private String id;
private double value;
#ManyToOne
#JoinColumn(name = "fk_methodology", referencedColumnName = "id")
private Methodology methodology;
#ManyToOne
#JoinColumn(name = "fk_impact_category", referencedColumnName = "id")
private ImpactCategory impactCategory;
#ManyToOne
#JoinColumn(name = "fk_impact_indicator", referencedColumnName = "id")
private ImpactIndicator impactIndicator;
#ManyToOne
#JoinColumn(name = "fk_source_info", referencedColumnName = "id")
private SourceInfo sourceInfo;
#Column(name = "version")
#Version
private Long version;
//ref
#ManyToOne
#JoinColumn(name = "fk_elementary_flow_base")
private ElementaryFlowBase elementaryFlowBase;
#ManyToOne
#JoinColumns({
#JoinColumn(
name = "process_base_id",
referencedColumnName = "process_base_id"),
#JoinColumn(
name = "reference_product_id",
referencedColumnName = "reference_product_id")
})
private ProcessTemplate processTemplate;
}
As I wrote I cannot understand why I had an errors log, probably I make a mistake in the relation or cascade but I'm not able to figure out the issue. If I import data they seem correctly inserted so I cannot understand the problem.
Thanks in advance for the help.
SOLVED, EDIT:
I solve only the error on ProcessTemplate-BaseEnvironmentalImpact adding a mappedBy.
I don't include a schema because I think it's autogenerated, I include these parameters in application properties:
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation = true
spring.jpa.hibernate.ddl-auto = create-drop
logging.level.org.hibernate = ERROR
spring.jpa.generate-ddl = true
I also solved other issues adding:
#JoinColumns({
#JoinColumn(name = "fk_pt_process_base_id", referencedColumnName = "process_base_id"),
#JoinColumn(name = "fk_pt_reference_product_id", referencedColumnName = "reference_product_id")
} )
I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
Consider the following database structure
I need to implement unidirectional one to one mapping like that (structure is simplified):
#Entity
#Table(name = "entity")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "customer_info", nullable = false)
private CustomerInfo customerInfo;
#OneToOne
#JoinColumn(name = "customer_credentials", nullable = false)
private CustomerCredentials customerCredentials;
// getter, setters etc
}
#Entity
#Table(name = "customer_info")
public class CustomerInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc
}
#Entity
#Table(name = "customer_credentials")
public class CustomerCredentials {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc
}
But somehow hibernate unable to differentiate that those joins are from different tables and throws such error:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.example.Customer column: customer_id (should be mapped with insert="false" update="false")
Important notice: I do not want to use #OneToOne(mappedBy = "customer") because I need cascade save functionality
You can use #JoinTable instead of #JoinColumn to solve your problem:
#Entity #Table(name = "entity") public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL, targetEntity = CustomerInfo.class)
#JoinTable(name = "customer_info", inverseJoinColumns = {#JoinColumn(name = "customer_id", nullable = false)})
private CustomerInfo customerInfo;
#OneToOne(cascade = CascadeType.ALL, targetEntity = CustomerCredentials.class)
#JoinTable(name = "customer_credentials", inverseJoinColumns = {#JoinColumn(name = "customer_id", nullable = false)})
private CustomerCredentials customerCredentials;
// getter, setters etc }
#Entity #Table(name = "customer_info") public class CustomerInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc }
#Entity #Table(name = "customer_credentials") public class CustomerCredentials {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getter, setters etc }
You could change the cascade strategy to any strategy you need. I just left CascadeType.ALL there as an example.
I'm trying to use Spring Data to perform joined queries but one of my tables has a Composite Key and I'm not sure how to map the entities.
Here is an analogy of the data model:
table: device
pk=model_id
pk=serial_id
...
table: device_settings
pk=device_settings_id
fk=model_id
fk=serial_id
...
Here is an analogy of the code, which doesn't compile due to a "mappedby" attribute that is isn't present.
#Entity
#Table(name = "device_settings")
public class DeviceSettings {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "device_settings_id")
private Long id;
// Pretty sure this is the problem
#OneToMany(targetEntity = Device.class, mappedBy = "deviceKey", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "model_id", referencedColumnName = "model_id"),
#JoinColumn(name = "serial_id", referencedColumnName = "serial_id")})
private List<Device> devices;
}
#Entity
#Table(name = "device")
public class Device {
#Id
private DeviceKey deviceKey;
}
...
}
#Embeddable
public class DeviceKey implements Serializable {
private static final long serialVersionUID = -1943684511893963184L;
#Column(name = "model_id")
private Long modelId;
#Column(name = "serial_id")
private Short serialId;
}
Associations marked as mappedBy must not define database mappings like #JoinTable or #JoinColumn
To achieve your scenario you have to define #ManyToOne:
#ManyToOne(cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "model_id", referencedColumnName = "model_id"),
#JoinColumn(name = "serial_id", referencedColumnName = "serial_id")})
private Device device;
This will end up model_id, serial_id, device_settings_id
or
Define #JoinColumn in Device Entity
Entities:
DeviceSettings :
#Entity
#Table(name = "device_settings")
public class DeviceSettings {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "device_settings_id")
private Long id;
#OneToMany( mappedBy = "deviceSettings", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
private List<Device> devices;
}
Device Entity :
#Entity
#Table(name = "device")
public class Device {
#EmbeddedId
private DeviceKey deviceKey;
#ManyToOne
#JoinColumn(name="device_settings_id")
private DeviceSettings deviceSettings;
//getters and setters
}
Note : you can decide which is the owner of the relationship and put your mappings accorindly either One Device has many device settings or other way around.