I have two classes ServiceFee and DeliveryFee.
#Entity
#Table(name = "service_fees")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorFormula(
"CASE WHEN is_delivery_fee = 1 THEN 'DELIVERY_FEE' ELSE 'SERVICE_FEE' end"
)
public class ServiceFee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "is_delivery_fee")
private Boolean isDeliveryFee;
...
}
and
#Entity
#DiscriminatorValue("DELIVERY_FEE")
public class DeliveryFee extends ServiceFee {
public Enums.DeliveryOption getOrderType() {
return Enums.DeliveryOption.DELIVERY;
}
public Boolean getIsDeliveryFee() {
return Boolean.TRUE;
}
}
My serviceFeeDAO has a method to return all ServiceFees (including the deliveryFees), which works as expected. I added method in the serviceFeeDAO which returns all the deliveryFee, please find code of the same below:
public List<DeliveryFee> getDeliveryFees() {
CriteriaBuilder builder = currentSession().getCriteriaBuilder();
CriteriaQuery<DeliveryFee> query = builder.createQuery(DeliveryFee.class);
Root<DeliveryFee> root = query.from(DeliveryFee.class);
return currentSession().createQuery(query)
.list();
}
invoking this method gave me an IllegalArgumentException that the DeliveryFee is not an entity.
Then I went ahead and created a DeliveryDAO class and added this method there. ad now the method returns an empty list.
I am looking to learn why I got the IllegalArgumentException in the first case, and why the method in the DeliveryFeeDAO won't identity the pick up the delivery fee entities.
Maybe take a look on this question, seems it has something to do with downcasting to base class: JPA 2 Criteria Query on Single Table Inheritance (Hibernate)
Hibernate appears to not be using the Id field for one specific class.
My setup looks like this:
#Data
#MappedSuperclass
public abstract class IdentifiableObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
#Data
#Entity
#Table(name = "A")
public class A extends IdentifiableObject {
private String field;
#ManyToOne(targetEntity = B.class)
private B b;
}
#Data
#Entity
#Table(name = "B")
public class B extends IdentifiableObject {
private TypeSomethingElse field;
#ManyToOne(targetEntity = C.class)
private C c;
#OneToMany(
cascade = CascadeType.ALL
)
private List<A> as;
}
#Data
#Entity
#Table(name = "C")
public class C extends IdentifiableObject {
#OneToMany(
cascade = CascadeType.ALL
)
private List<B> bs;
}
In my code I save an object C to the database, use the data in the database to perform some calculations, create a jasper report and delete the object C from the database again. When deleting the C object I was getting this error:
org.hibernate.HibernateException: More than one row with the given identifier was found: A(field="something")
This Exception is thrown in the class:
public abstract AbstractEntityLoader {
protected Object load(
SharedSessionContractImplementor session,
Object id,
Object optionalObject,
Serializable optionalId,
LockOptions lockOptions){
// Some code
}
}
When the load method is triggered for the B objects, the id passed to the load method is the value of the field id. Whenever it is triggered for the A object it passes a A object with only the field attribute filled in, Our id is null. I personally would asume the method would use the Id field in both cases but it does not. Anyone knows what's happening here?
JPA-Repositories:
I use auto implemented interfaces for deleting.
public interface CRepository extends IdentifiableObjectRepository<C>, JpaRepository<C, Integer> {
C findById(Integer cId);
}
PS: The #Data anotation is part of Lombok to provide getters and setters and some other useful methods.
PPS: I have been able to get it to work by adding a new delete method to the JpaRepository: 'void deleteById(Integer id)', so it seems there is an issue with the default CRUDRepository delete method. This feels like a work around and I would still like to know what the reason is for this issue.
#MappedSuperClass
public abstract class BaseMappedSuperClass {
#EmbeddedId
private EmbeddedId id;
}
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "typeCol")
public abstract class Vehicle extends BaseMappedSuperClass{
private String name;
}
#Entity(name = "Cycle")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Cycle")
public class Cycle extends Vehicle {
private String bellType;
}
#Entity(name = "Bus")
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#DiscriminatorValue(value = "Bus")
public class Bus extends Vehicle {
private String gearType;
}
I have the above entity structure and if I try to do an insert op on the entity Cycle or Bus, it fails inconsistently, because of the missing primary key field (id).
When I tried to debug the JPA codebase, I figured that the tenant discriminator, which is tenant_id in my case is not appended to the table name prefix for the embeddedId field 'Id' and the discriminator column field 'typeCol'.
What is more interesting is that this behavior is not consistent. If I restart my application and try, it works. If I restart again and try,it does not work.
Any help would be appreciated. Version of eclipse link used is 2.5.1.
What is the logic behind the order in which the entities are processed to initialize the metadata?
Sorry for long title..
I have the following error when querying a TableColumn entity:
Could not set field value
[org.comp.domain.data.ConstantParameterType#18c81fe5] value by
reflection : [class org.comp.data.AnalogParameter.analogParameterType]
setter of org.comp.data.AnalogParameter.analogParameterType; nested
exception is org.hibernate.PropertyAccessException: Could not set
field value [org.comp.data.ConstantParameterType#18c81fe5] value by
reflection : [class
org.comp.domain.data.AnalogParameter.analogParameterType] setter of
org.comp.domain.data.AnalogParameter.analogParameterType
My model contains two distincts 'single table per class' hierarchies having Parameter and ParameterType as superclasses. Each subclasse of Parameter hierarchy is mapped with a subclass of ParameterType hierarchy through #ManyToOne associations.
Here is an extract of my model with involved entities (unrelated fields ommitted):
// `Parameter` Single Table Per Class hierarchy
#Entity
#Table(name="parameters")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class Parameter {
}
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private AnalogParameterType analogParameterType;
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
}
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameter extends Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id")
private ConstantParameterType constantParameterType;
public ConstantParameterType getConstantParameterType() {
return constantParameterType;
}
public void setConstantParameterType(ConstantParameterType constantParameterType) {
this.constantParameterType = constantParameterType;
}
}
// `ParameterType` Single Table Per Class hierarchy
#Entity
#Table(name="parameters_types")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
public abstract class ParameterType { }
#Entity
#DiscriminatorValue(value="analog")
public class AnalogParameterType extends ParameterType { }
#Entity
#DiscriminatorValue(value="constant")
public class ConstantParameterType extends ParameterType {
}
Here is the TableColumn which is mapped with Parameter superclass through a #ManyToOne association:
#Entity
#Table(name="tables_columns")
public class TableColumn {
#ManyToOne
#JoinColumn(name="parameter_id")
private Parameter parameter;
public Parameter getParameter() {
return parameter;
}
}
And here is the generated SQL when querying A TableColumn Entity:
select tablecolum0_.id as id1_12_0_, tablecolum0_.created_at as
created_2_12_0_, tablecolum0_.is_optional as is_optio3_12_0_,
tablecolum0_.parameter_id as paramete6_12_0_, tablecolum0_.position as
position4_12_0_, tablecolum0_.updated_at as updated_5_12_0_,
parameter1_.id as id2_8_1_, parameter1_.category as category1_8_1_,
parameter1_.created_at as created_3_8_1_, parameter1_.device_id as
device_14_8_1_, parameter1_.effective_date as effectiv4_8_1_,
parameter1_.expiry_date as expiry_d5_8_1_, parameter1_.fqn as
fqn6_8_1_, parameter1_.height_from_the_ground as height_f7_8_1_,
parameter1_.label as label8_8_1_, parameter1_.name as name9_8_1_,
parameter1_.resolution_label as resolut10_8_1_, parameter1_.updated_at
as updated11_8_1_, parameter1_.parameter_type_id as paramet15_8_1_,
parameter1_.data_validity_period as data_va12_8_1_,
parameter1_.resolution as resolut13_8_1_, device2_.id as id1_1_2_,
device2_.created_at as created_2_1_2_, device2_.device_type_id as
device_t8_1_2_, device2_.fqn as fqn3_1_2_, device2_.label as
label4_1_2_, device2_.name as name5_1_2_, device2_.notes as
notes6_1_2_, device2_.parent_device_id as parent_d9_1_2_,
device2_.plant_id as plant_i10_1_2_, device2_.updated_at as
updated_7_1_2_, constantpa3_.id as id2_9_3_, constantpa3_.created_at
as created_3_9_3_, constantpa3_.description as descript4_9_3_,
constantpa3_.is_signed as is_signe5_9_3_, constantpa3_.label as
label6_9_3_, constantpa3_.name as name7_9_3_, constantpa3_.updated_at
as updated_8_9_3_ from tables_columns tablecolum0_ left outer join
parameters parameter1_ on tablecolum0_.parameter_id=parameter1_.id
left outer join devices device2_ on parameter1_.device_id=device2_.id
left outer join parameters_types constantpa3_ on
parameter1_.parameter_type_id=constantpa3_.id where tablecolum0_.id=1
I'm using Hibernate 5.0.11 with MySQL in a Spring Boot 1.4.1 / Data Rest project
EDIT
I tried in a vanilla Maven/Hibernate project using the same database. I have the same error. If i query Parameter objects directly, it's OK, but i get the error if i query TableColumn:
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// The following works
List<Parameter> ps = entityManager.createQuery("from Parameter", Parameter.class).getResultList();
for (Parameter p: ps) {
System.out.println(p);
}
// But if i replace with following, i get the same error as reported
// in the beginning of this question
List<TableColumn> tcs = entityManager.createQuery("from TableColumn", TableColumn.class).getResultList();
for (TableColumn tc: tcs) {
System.out.println(tc);
}
entityManager.getTransaction().commit();
entityManager.close();
1) Your category is of size=7, but you are setting a value of 8 characters i.e, constant
#DiscriminatorColumn(name="category", length=7)//change to more than 7
Because, #DiscriminatorValue(value="constant")//here the length is 8
You can use the below code for Discriminator column:
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)//no need of length attribute
2) verify that for the property private AnalogParameterType analogParameterType; you have below setter and getter methods.
//setter
public void setAnalogParameterType(AnalogParameterType analogParameterType) {
this.analogParameterType = analogParameterType;
}
//getter
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
3) Instead of
#ManyToOne
#JoinColumn(name="parameter_type_id")
use #ManyToOne(cascade=CascadeType.ALL) on getters.
For example:
#ManyToOne(cascade = CascadeType.ALL)
public AnalogParameterType getAnalogParameterType() {
return analogParameterType;
}
So i solved my issue by mapping the two superclasses Parameter and ParameterType with insertable/updatable=false:
#Entity
#Table(name="parameters")
#DiscriminatorColumn(name = "category", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorOptions(force=true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Parameter {
#ManyToOne
#JoinColumn(name="parameter_type_id", insertable = false, updatable = false)
private ParameterType parameterType;
}
It appears that enforcing the association at the superclass level does the trick. I don't know if it is a pure side effect or a design need. I have not found any resources on the subject.
I am actively learning JPA and EJBs and I came to the following situation: I have one EJB (named SecondController) which implements a method for persisting an entity. Let's call it SecondEntity. There is a requirement that when the entity is persisted its field dateInserted is set to the current date (in practice the requirements may be more complex - I want to know if the concept is OK or if I am going totally in the wrong direction).
Now I define a new entity - let's call it FirstEntity - which contains SecondEntity as an attribute. I implement another EJB which implements a method for persisting FirstEntity - so each time FirstEntity is persisted, SecondEntity must be persisted too. I know I can use #ManyToOne(optional = false, cascade = CascadeType.PERSIST) on the secondEntity attribute in FirstEntity (that would do auto persist). But remember, there is a dateInserted requirement and logic is already implemented. The idea is to reuse the already implemented method for persisting SecondEntity in SecondController by injecting it into FirstController.
#Entity
public class SecondEntity implements Serializable
{
}
#Entity
public class FirstEntity implements Serializable
{
#JoinColumn(name = "second_id", referencedColumnName = "second_id")
#ManyToOne(optional = false)
private SecondEntity secondEntity;
}
#Stateless
public class SecondController implements Serializable
{
#PersistenceUnit(name = "PU")
private EntityManagerFactory emf;
public SecondController()
{
}
public void create(SecondEntity secondEntity)
{
EntityManager em = emf.createEntityManager();
try
{
secondEntity.setDateInserted(new Date(System.currentTimeMillis()));
em.persist(secondEntity);
}
finally
{
em.close();
}
}
}
#Stateless
public class FirstController implements Serializable
{
#PersistenceUnit(name = "PU")
private EntityManagerFactory emf;
#EJB
private SecondController secondController;
public FirstController()
{
}
public void create(FirstEntity firstEntity)
{
EntityManager em = emf.createEntityManager();
try
{
secondController.create(firstEntity.getSecondEntity());
em.persist(firstEntity);
}
finally
{
em.close();
}
}
}
The point of the sample code above is to call secondController.create(firstEntity.getSecondEntity()). Is this OK or is it considered a bad practice? I have two such cases in my application and one works (SecondEntity is optional - can be null), and one which does not (SecondEntity is mandatory - cannot be null). The code fails at em.persist(firstEntity) by saying:
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST
If I replace the mentioned call with em.persist(firstEntity.getSecondEntity) the code works fine.
How can I solve this problem?
You shouldn't set the dateInserted field in your EJB. Do it in a PrePersist event hook. This way the persistence provider will make sure your hook is always called automatically before persisting an entity. Then you can let persist to be cascaded from parent entity to the other (#ManyToOne(optional = false, cascade = CascadeType.PERSIST)).
#Entity
public class SecondEntity implements Serializable {
#PrePersist
public void prePersist() {
if (dateInserted == null) {
dateInserted = new Date();
}
}
}