JPA/Hibernate inheritance while loading: IllegalArgumentException, try to set wrong subclass - jpa

I'm using JBoss 6.1, I have the a JPA entagled situation, as a result I got the following error message:
IllegalArgumentException: Can not set EquipmentB field EquipmentCycleB.equipment to EquipmentA
suppose that each entity has an #Id annotated field:
A first hierarchy
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DIS", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class Equipment { ... }
#Entity
#DiscriminatorValue("A")
public class EquipmentA extends Equipment { ... }
#Entity
#DiscriminatorValue("B")
public class EquipmentB extends Equipment { ... }
A second hierarchy
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DIS", discriminatorType = DiscriminatorType.STRING, length = 10)
public abstract class EquipmentCycle {
...
}
#Entity
#DiscriminatorValue("A")
public class EquipmentCycleA extends EquipmentCycle {
#JoinColumn(name = "EQUIPMENT_ID", referencedColumnName = "ID")
#ManyToOne
private EquipmentA equipment;
...
}
#Entity
#DiscriminatorValue("B")
public class EquipmentCycleB extends EquipmentCycle {
#JoinColumn(name = "EQUIPMENT_ID", referencedColumnName = "ID")
#ManyToOne
private EquipmentB equipment;
...
}
So far nothing strange, go on, the class say Status
#Entity
public class State {
#JoinColumn(name = "ENTITY_ID", referencedColumnName = "ID", nullable = false)
#ManyToOne
private EnityWithState enityWithState;
#JoinColumn(name = "EQUIPMENT_ID", referencedColumnName = "ID")
#ManyToOne
private Equipment equipment;
#JoinColumn(name = "EQUIPMENT_CYCLE_ID", referencedColumnName = "ID")
#ManyToOne
private EquipmentCycle equipmentCycle;
...
}
and the class which the state belong to
public class EnityWithState {
...
#OneToMany(mappedBy = "enityWithState", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<State> stateHistory;
...
}
Don't ask me why, I have just found it.
After some time the DB store the following data
Table Equipment
ID DIS
--------------------------------
1 A ...
2 B ...
Table EquipmentCycle
ID DIS EQUIPMENT_ID
--------------------------------
1 A 1
2 B 2
Table State
ID ENTITY_ID EQUIPMENT_ID EQUIPMENT_CYCLE_ID
---------------------------------------------
1 1 1 1
2 1 2 2
And finally, when I try lo load the EnityWithState with id 1, I got the following error:
IllegalArgumentException: Can not set EquipmentB field EquipmentCycleB.equipment to EquipmentA
Does anyone have any idea? I googled but I found nothing.
Could it be an Hibernate/JPA bug?
Thanks in advance for the help

Related

JPArepository method with ManyToOne not return related data

I have two entities using a OneToMany and ManyToOne relationship:
#Entity
#Table(name = TableName.PERSON)
public class Person {
#Id
#Column(name = FieldName.DB_KEY)
public String dbKey;
#Column(name = FieldName.ENTITY_ID)
public String entityId;
#Column(name = FieldName.SORT_KEY)
public String sortKey;
#JoinColumn(name = FieldName.ENTITY_ID, referencedColumnName =
FieldName.ENTITY_ID, insertable = false, updatable = false)
#ManyToOne(fetch = FetchType.EAGER)
#JoinFetch(value = JoinFetchType.INNER)
public WLEntity entity;
}
#Entity
#Table(name = TableName.WL_ENTITY)
public class WLEntity {
#Id
#Column(name = FieldName.ENTITY_ID)
public String entityId;
#Column(name = FieldName.HAS_ADDRESS)
public boolean hasAddress;
#OneToMany(mappedBy = "entity", targetEntity = PersonIndex.class, cascade = CascadeType.ALL)
public List<Person> persons;
}
And a JPA-Repository defining one findBy... Method:
#Repository
public interface PersonRepository extends JpaRepository<Person, String> {
List<Person> findBySortKeyStartingWith(String sortKey);
}
If I call this method I can see in the console:
SELECT t1.DB_KEY, t1.ENTITY_ID, t1.SORT_KEY, t0.ENTITY_ID, t0.HAS_ADDRESS FROM WL_ENTITY t0, PERSON t1 WHERE (t1.SORT_KEY LIKE ? AND (t0.ENTITY_ID = t1.ENTITY_ID))
So the join I want is correct executed, but in the returned data the entity field is still null but all other fields are filled:
List<Person> persons = personRepository.findBySortKeyStartingWith("Sort");
Person person = persons.get(0);
person.entity == null but person.entityId is correctly filled.
So what I have to do, to get person.entity filled?
I use spring boot with eclipselink jpa 2.6.4

Criteria API query with non-direct relation

Good day!
There is SQL query that finely works:
select oi.nameshort from creditrequest c
join users u on u.id=c.user_id
join peoplemanagers pm on pm.people_id=u.people_id
-- join organiztion o on pm.organization_id=o.id
join organizationinfo oi on oi.organization_id=pm.organization_id
where oi.nameshort like 'Hydro%'
The problem arises when I try to translate SQL to JPA Criteria API. I need like restriction on table organizationinfo, but there is no direct reference from organization to it.
I try
pb.like(root.get(CreditRequestEntity_.userId)
.get(UsersEntity_.peopleId)
.get(PeopleEntity_.id)
.get(PeopleManagerEntity_.organizationId)
.get(OrganizationInfoEntity_.organizationId),
filter.getOrganization());
but it fails on .get(PeopleEntity_.id).
How to solve problem on Criteria API?
Classes:
#Entity
#Table(name = "creditrequest")
public class CreditRequestEntity {
...
#ManyToOne
#JoinColumn(name = "borrower_id")
private BorrowerEntity borrower;
...
}
#Entity
#Table(name = "borrower")
public class BorrowerEntity {
...
#ManyToOne
#JoinColumn(name = "organization_id")
private OrganizationEntity organizationId;
...
}
#Entity
#Table(name = "organization")
public class OrganizationEntity { ... }
#Entity
#Table(name = "organizationinfo")
public class OrganizationInfoEntity { ...
#Column(name = "nameshort")
private String nameShort;
#ManyToOne
#JoinColumn(name = "organization_id")
private OrganizationEntity organizationId;
...
}
Any class has field id as primary key.
if (filter.getOrganization() != null) {
List<Expression<String>> expressions = new ArrayList<>();
Subquery<OrganizationInfoEntity> psq = query.subquery(OrganizationInfoEntity.class);
Root<OrganizationInfoEntity> orgInfoRoot = psq.from(OrganizationInfoEntity.class);
psq.select(orgInfoRoot);
Join<OrganizationEntity, OrganizationInfoEntity> borrowerOrganizationInfoJoin = root.join(CreditRequestEntity_.borrower)
.join(BorrowerEntity_.organizationId).join(OrganizationEntity_.organizationInfo);
PredicateBuilder pipb = new PredicateBuilder(builder);
pb.add(builder.equal(borrowerOrganizationInfoJoin.get(OrganizationInfoEntity_.isActive), ActiveStatus.ACTIVE));
expressions.add(borrowerOrganizationInfoJoin.get(OrganizationInfoEntity_.nameShort));
pipb.like(filter.getOrganization(), expressions.toArray(new Expression[]{}));
psq.where(pipb.getWherePredicates());
pb.add(builder.exists(psq));
}

one side set in many-to-many relation

I have three database tables: Customer, Product and PurchaseOrder (for mapping). I am using openjpa for peristence in java rest application.
To all of the tables I have corresponding entities:
Customer
#Entity
#Table(name = "customer")
#XmlRootElement
#NamedQueries({...})
public class Customer implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customerId")
private Collection<PurchaseOrder> purchaseOrderCollection;
Product
#Entity
#Table(name = "product")
#XmlRootElement
#NamedQueries({...})
public class Product implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productId")
private Collection<PurchaseOrder> purchaseOrderCollection;
PurchaseOrder
#Entity
#Table(name = "purchase_order")
#XmlRootElement
#NamedQueries({..})
public class PurchaseOrder implements Serializable {
...
#Id
#Basic(optional = false)
#Column(name = "order_num")
private Integer orderNum;
#JoinColumn(name = "customer_id", referencedColumnName = "customer_id")
#ManyToOne(optional = false)
private Customer customer;
#JoinColumn(name = "product_id", referencedColumnName = "product_id")
#ManyToOne(optional = false)
private Product product;
What is the best way to get all the customers who ordered a product with specific id?
I could create namedQuery, I could build criteria with joins etc. But i think there could be a better way how to make use of the mapping entity (what would be point of this entity otherway?). Something like setting the productId to the purchaseOrder entity and then fetch all the customers via purchaseOrderCollection in customer entity? But i cannot figure it out. Is there other way than custom/named query or criteria building?
Thanks.
ok I figured it out, it can be this way
long productId = //get the id
Product product = entityManager.find(Product.class, productId);
Collection<PurchaseOrder> purchaseOrderCollection = product.getPurchaseOrderCollection();
if (purchaseOrderCollection != null) {
List<Integer> customers = new ArrayList<>(product.getPurchaseOrderCollection().size());
for (PurchaseOrder purchaseOrder : product.getPurchaseOrderCollection()) {
customers.add(purchaseOrder.getCustomerId());
}
return customers;
} else {
return Collections.EMPTY_LIST; // or null;
}
feel free to offer better sollution :)

using #Embedabble with a foreign key and manyToMany relation

I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.

JPA query many to one association

I want to build the following pseudo query
Select a From APDU a where a.group.id= :id
group is a field in APDU class of the type APDUGroup.class.
I just want to get a list of APDUs based on APDUGroup's id.
How do i do that using a standard JPA query?
UPDATE
Yes, I have tried the above query and tried other variations for hours before posting in S/O. Here is the generated SQL for the query above:
SELECT t1.ID, t1.status, t1.type, t1.modified, t1.response, t1.expectedSize, t1.created, t1.description, t1.sequence, t1.name, t1.command, t1.recurring, t1.auth, t1.createdBy, t1.APDUGroup, t1.modifiedBy FROM APDUGroup t0, APDU t1 WHERE ((t0.ID = ?) AND (t0.ID = t1.APDUGroup))
The query looks okay but nothing get selected from my table.
There are at least 100 APDUs with APDUGroup = 1 in my test database.
I'm using eclipselink as the JPA provider.
Given the following Entities:
#Entity
public class APDU implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private APDUGroup group;
//...
}
#Entity
public class APDUGroup implements Serializable {
#Id
#GeneratedValue
private Long id;
//...
}
The following query will return a list of APDUs for a given APDUGroup id:
select a from APDU a where a.group.id = :id
Oh, wait, that's your query :)
Entity 1:
#Entity
#Getter
#Setter
#Table(name = "invoices")
public class Invoice implements Serializable {
#Id
#GeneratedValue
#Column(name = "invoice_id", updatable = false, nullable = false)
private Long invoiceId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id", referencedColumnName = "person_id", insertable = false, updatable = false, nullable = false)
private Person person;
//...
}
Entity 2:
#Entity
#Getter
#Setter
#Table(name = "people")
public class Person implements Serializable {
#Id
#GeneratedValue
#Column(name = "person_id", updatable = false, nullable = false)
private Long personId;
//...
}
Finally, Your Data Access Object (JPA Repository)
#Repository
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
#Query(value="SELECT x FROM Invoice x WHERE x.person.personId = :myPersonId")
List<Invoice> findInvoiceByPersonId (long myPersonId);
}
I hope this example has been helpful :)