hibernate OneToOne with EmbeddedId - java

The "TypeMismatchException: Provided id of the wrong type" error thrown when tried to merge detached entity. It works if the object wasn't detached. It also works if ids aren't #EmbeddedId.
A sample repo can be found here https://github.com/joes-code/hibernate-map
// Asset.java
#Entity
#Table(name = "asset")
public class Asset {
#EmbeddedId
private AssetId id;
#Column(name = "asset_cost"
private BigDecimal price;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "asset_id", referencedColumnName = "asset_id", nullable = false, insertable = false, updatable = false, foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT))
private AssetDetail assetDetail;
}
// AssetId.java
#Embeddable
public class AssetId {
#Column(name = "asset_id", nullable = false)
private Integer assetId;
}
// AssetDetail.java
#Entity
#Table(name = "asset_detail")
public class AssetDetail {
#EmbeddedId
private AssetDetailId id;
#Column(name = "description", length = 35)
private String description;
}
// AssetDetailId.java
#Embeddable
public class AssetDetailId {
#Column(name = "asset_id", nullable = false)
private Integer assetId;
}
I'm using Hibernate 5.4.3.Final
Any ideas what I did wrong? It seems that Hibernate is assuming Asset and AssetDetail share the same Id class?

Related

Spring Boot JPA - ManyToOne relationship causes extra sql

I have 3 objects with simple relationship which looks as follows:
University:
#Entity
public class University {
#Id
#GeneratedValue
private Long id;
private String name;
}
Faculty:
#Entity
public class Faculty {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "university_id", nullable = false)
private Long universityId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinFetch(value = JoinFetchType.OUTER)
#JoinColumn(name = "university_id", insertable = false, updatable = false, nullable = false)
private University university;
}
Specialty:
#Entity
public class Specialty {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "faculty_id", nullable = false)
private Long facultyId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinFetch(value = JoinFetchType.OUTER)
#JoinColumn(name = "faculty_id", insertable = false, updatable = false, nullable = false)
private Faculty faculty;
}
I am using EclipseLink and Spring CrudRepository to operate with these entities.
When i call
specialtyRepository.findAll();
i get sql
SELECT * FROM specialty LEFT OUTER JOIN faculty ON (faculty.ID = specialty.faculty_id) ...
and extra sql like
SELECT * FROM university WHERE ((ID = ?)) ...
I want to prevent this sql request;
Could someone tell me how to resolve this issue?
Thank you for any tips
For lazy loading in #ManyToOne i must enable dynamic weaving in EclipseLink:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving
I solved this problem via inheritance;
I have created the base instance without relationship;
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class FacultyBase {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "university_id", nullable = false)
private Long universityId;
}
and instance with relationship
#Entity
public class Faculty extends Faculty {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "university_id", insertable = false, updatable = false, nullable = false)
private University university;
}
As a result, in Specialty instance i use FacultyBase instead of Faculty

Inheritance in Hibernate. Inserting example

I want to make connection between Product class and Laptop
Product class
#Entity
#Table(name = "products")
#Inheritance(strategy = InheritanceType.JOINED)
public class Product {
#Id
#Column(unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(nullable = false, unique = true)
private String name;
#Column(nullable = false)
private BigDecimal cost;
#Column(nullable = false)
private int count;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "productStatus", nullable = false)
private ProductStatus productStatus;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "productManufacture", nullable = false)
private ProductManufacture productManufacture;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "productCategory", nullable = false)
private ProductCategory productCategory;
and
Laptop class
#Entity
#Table(name = "laptops")
public class Laptop extends Product {
#Id
#Column(unique = true, nullable = false)
private int id;
#Column(nullable = false)
private String processor;
private int videoMemory;
#Column(nullable = false)
private int ram;
But my varient doesnt work...( How can I do it right
And how can I insert into database some data?(example please)
Laptop is extending Product class and inherits all the fields from the parent class including ID. You are defining ID again on the Laptop class. Inheritance Strategy is also JOINED which means it handles the Id generation both for the parent and the child class. Remove the Id field and its annotations.

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

Hibernate creating Composite Key instead of using Identifier

I have an Entity FooBar which serves as the #ManyToMany join table for Foo and Bar entities including some additional information.
#Entity
#Table(name = "foo_bar")
public class FooBar
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
protected Long id;
#Column(name = "someInfo", nullable = true)
private String someInfo;
#ManyToOne(optional = false)
private Foo foo;
#ManyToOne(optional = false)
private Bar bar;
//getters, setters, and toString()
}
#Entity
#Table(name = "foo")
public class Foo
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
protected Long id;
#OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<FooBar> fooBars;
//Foo has a number of other fields
#Column(name = "orderIndex", nullable = false)
private int orderIndex;
#Column(name = "upgradeDirection", nullable = false)
#Enumerated(EnumType.STRING)
private Order direction;
#ManyToOne(optional = false)
private SomeEntity e;
//getters, setters, and toString()
}
#Entity
#Table(name = "bar")
public class Bar
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", unique = true, nullable = false)
protected Long id;
#OneToMany(mappedBy = "bar") //TODO specify a cascade and fetch attribute
private Set<FooBar> fooBars;
//Bar contains a number of other fields
#Column(name = "value", nullable = false)
private String value;
//getters, setters, and toString()
}
When Hibernate creates the table is has columns 'id', 'someInfo', 'foo_id', and 'bar_id'. 'foo_id' and 'bar_id' are used in as a composite key instead of using the 'id' field, any idea why?

Hibernate List Mapping Annotation

Well i have this problem
These are my tables
this is my code for "Compra"
#Entity
#Table(name = "compra")
public class Compra implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "codigo", unique = true, nullable = false)
private int codigo;
#ManyToOne
#JoinColumn(name = "codProveedor", nullable = false)
private Proveedor proveedor;
#Column(name = "tipoComprobante", nullable = false)
private String tipoComprobante;
#Temporal(TemporalType.DATE)
#Column(name = "fechaFactura", nullable = false)
private Date fechaFactura;
#Temporal(TemporalType.DATE)
#Column(name = "fechaLlegada", nullable = false)
private Date fechaLlegada;
#Column(name = "serie", nullable = false)
private String serie;
#Column(name = "numero", nullable = false)
private int numero;
#Column(name = "importe", nullable = false)
private double importe;
#Column(name = "vigencia", nullable = false)
private boolean vigencia = true;
#ElementCollection
private List<DetalleCompra> lstDetalle = new ArrayList<DetalleCompra>();
// getters and setters ...
And this is my code for "DetalleCompra"
#Entity
#Table(name = "detalleCompra")
public class DetalleCompra implements Serializable {
#Id
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name = "property", value = "compra"))
#Column(name = "codCompra", nullable = false)
private int codCompra;
#ManyToOne
#JoinColumn(name = "codPresentacion", nullable = false)
private Presentacion presentacion;
#Column(name = "imei", nullable = false)
private String imei;
#Column(name = "simcard", nullable = false)
private String simcard;
getters and setters ...
Well everything looks fine, but when i want to save i have this problem
org.hibernate.TransientObjectException: object references an unsaved transient instance – save the transient instance before flushing: DetalleCompra
well it is clear because when i want to save Compra and DetalleCompra, the second table expect the fk value
public void registrar(Compra compra) {
try {
session = HibernateUtil.getSessionFactory().openSession();
trans = session.beginTransaction();
session.save(compra);
trans.commit();
} catch (Exception e) {
trans.rollback();
throw e;
} finally {
session.close();
}
}
Well the pk of table "compra" is generated well but for the other table does not recognized this value autogenerated, why?, how can i solve that?
#ElementCollection
Defines a collection of instances of a basic type or embeddable class.
Must be specified if the collection is to be mapped by means of a
collection table.
You use wrong annotation to represent relation. There is one to many relation between Compra and DetalleCompra.
You should change #ElementCollection annotation to #OneToMany. Do not forget to specify join columns #JoinColumn(name="codCompra"). I assume that Presentacion is properly mapped.
See also
Unidirectional Mapping vs. Bidirectional Mapping
mappedBy attribute
#OneToMany annotation
#ElementCollection annotation

Categories