Spring Data - Specification Join with hierarchy - java

I have problems with a specification, in which I am trying to do a left Join between two classes, that are children of two abstract classes.
I have a Hierarchy like this:
#Entity
#Table(name = "product")
public abstract class Product {
#Id
#SequenceGenerator(name = "product_seq", sequenceName = "product_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "product_seq")
#Column(updatable = false)
private Long id;
private String name;
private String brand;
}
and
#Entity
#Table(name = "product")
public class Fridge extends Product {
#Id
#SequenceGenerator(name = "product_seq", sequenceName = "product_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "product_seq")
#Column(updatable = false)
private Long id;
private String otherField;
...
}
In addition, I have other classes like this:
#Entity
#Table(name = "offer")
public abstract class Offer {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="PRODUCT_ID")
private Product product;
...
}
and
#Entity
#JsonDeserialize(using = FridgeOfferDeserializer.class)
#Table(name = "offer")
public class FridgeOffer extends Offer {
#Id
#SequenceGenerator(name = "offer_gen", sequenceName = "offer_seq", allocationSize = 1)
#GeneratedValue(strategy= GenerationType.IDENTITY, generator = "offer_gen")
private Long id;
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="PRODUCT_ID")
private Fridge product;
...
}
In my specification, I have a code like this:
#Override
public Predicate toPredicate(Root<FridgeOffer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Join<FridgeOffer, Fridge> join = root.join("product", JoinType.LEFT);
Path<String> expression = join.get(searchCriteria.getKey());
...
}
but every time that I perform a request filtering for a child's field, I have the following error:
Unable to locate Attribute with the the given name [otherField] on this ManagedType [com.fishingoffers.main.abstracts.Product]; nested exception is java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [otherField] on this ManagedType [com.fishingoffers.main.abstracts.Product]
I tried with other_field (the name of the column) but I have the same problem. The most extrange part of the message, is that hibernate seems to be doing the lookup in the Product class, instead of Fridge, that is the one that I choose in the specification. If I try with columns defined in Product, everything is OK. What am I doing wrong?

Related

MappedSuperclass - moving SequenceGenerator id up from all child classes to parent class

I was following MappedSuperclass - Change SequenceGenerator in Subclass to move id field up to the AbstractEntity from all the child entity classes but also keeping their distinct sequences.
All entities extend a superclass called AbstractEntity like this:
#MappedSuperclass
#EntityListeners(RecordAuditorListener.class)
public class AbstractEntity implements Serializable {
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_GEN")
protected Integer id;
...
The child entity named Product:
#Entity
#SequenceGenerator(name = "ID_GEN", sequenceName = "SQ_PRODUCT", allocationSize = 1)
#Table(name = "PRODUCT")
#EntityListeners({OnProductChangeSyncRequestCreatorListener.class})
#XmlRootElement
public class Product extends AbstractEntity implements Attributable {
//This id field was removed from here
// #Id
// #Column(name = "ID", nullable = false)
// #GeneratedValue(generator = "SQ_PRODUCT")
// #SequenceGenerator(name = "SQ_PRODUCT", sequenceName = "SQ_PRODUCT", allocationSize = 1)
// private Integer id;
...
The child entity named Partner:
#Entity
#SequenceGenerator(name = "ID_GEN", sequenceName = "SQ_PARTNER", allocationSize = 1)
#Table(name = "PARTNER")
#EntityListeners({OnProductChangeSyncRequestCreatorListener.class})
#XmlRootElement
public class Partner extends AbstractEntity implements Attributable {
//This id field was removed from here
// #Id
// #Column(name = "ID", nullable = false)
// #GeneratedValue(generator = "SQ_PARTNER")
// #SequenceGenerator(name = "SQ_PARTNER", sequenceName = "SQ_PARTNER", allocationSize = 1)
// private Integer id;
...
When running the application I get this error message.
Exception Description: Conflicting annotations with the same name [ID_GENERATOR] were found. The first one [#javax.persistence.SequenceGenerator({allocationSize=1, name=ID_GENERATOR, sequenceName=sq_PARTNER})] was found within [...] and the second [#javax.persistence.SequenceGenerator({allocationSize=1, name=ID_GENERATOR, sequenceName=sq_PR_PRODUCT})] was found within [...]. Named annotations must be unique across the persistence unit..
In the related question it says that this is an incorrect solution in eclipselink 2.6.2. I am using eclipselink 2.6.9. is there a way that it can be done?

JPA Mapping #EmbeddedId with ManyToOne relationship

so I searched for the answers for my problem on the internet but didn't find something that helped, basically a need to have a ManyToOne Relationship between two classes, of which one of them has an EmbeddedId, I'm going to leave the code here and the error message that it gives (I'm using wildfly to run the server).
public class InventoryPK implements Serializable {
#ManyToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private Item itemId;
#ManyToOne
#JoinColumn(name="CD_EMPRESA")
private Company company;
}
#Entity
#Table(name = "inventario", schema = "mxnextmob")
public class Inventory extends BaseModel {
#EmbeddedId
private InventoryPK id;
#SequenceGenerator(schema = "mxnextmob", name = "inventory_sequence", sequenceName = "inventory_sequence", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "inventory_sequence")
private Integer inventory;
#Column
private BigDecimal quantity;
#Column
private BigDecimal weight;
}
public class Company extends BaseModel {
#Id
#SequenceGenerator(schema = "mxnextmob", name = "company_sequence", sequenceName = "company_sequence", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "company_sequence")
private Integer code;
#Column
private String name;
#OneToMany(mappedBy = "company")
private List<UserSeller> userSeller;
#OneToMany(mappedBy = "id.company")
private List<Inventory> inventories;
}
and the error is as follows:
service jboss.persistenceunit."mxnext-mobile.war#mxnextmobileDS":
org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:
br.com.maxiconsystems.mobile.model.Inventory.company in
br.com.maxiconsystems.mobile.model.Company.inventory
There are a few ways to map what you seem to have as a table, but I'd recommend Inventory be changed to something like:
public class Inventory extends BaseModel {
#Id
#SequenceGenerator(schema = "mxnextmob", name = "inventory_sequence", sequenceName = "inventory_sequence", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "inventory_sequence")
private Integer inventory;
#Embedded
private InventoryPK alternateKey;
#Column
private BigDecimal quantity;
#Column
private BigDecimal weight;
}
This allows you to use the inventory Integer as its primary key; this simplifies any future references you may need to add to Inventory, as foreign keys would be required in JPA to reference all its ID columns.

mappedBy reference an unknown target entity property with abstraction

I have the following classes and I'm trying to store all the derived classes in one table (aircraft).
But I get the above error, of course, I was searching for a solution for a while, and tried to play with the mappedBy name value as well...
What am I missing?
#Entity // for hibernate
#Table // for database
public class Airline {
#Id
#SequenceGenerator(
name = "airline_sequence",
sequenceName = "airline_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "airline_sequence"
)
private long airline_id;
#ManyToOne
#JoinColumn(name = "airport_id")
private Airport homeAirport;
#OneToMany(mappedBy = "aircraft", cascade = CascadeType.ALL)
private List<Aircraft> airplaneList;
#Inheritance
#DiscriminatorColumn
#Entity
public abstract class Aircraft implements Serializable {
#Id
#SequenceGenerator(
name = "aircraft_sequence",
sequenceName = "aircraft_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "aircraft_sequence"
)
// Data members
private long aircraft_id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "airline_id")
private Airline airline;
#Version
private Long version;
#Entity // for hibernate
#Table // for database
#DiscriminatorValue("Airplane")
public class Airplane extends Aircraft {
//Constructors here

How to reference a parent's id in a child's id with JPA/Hibernate?

Given a table (MY_TABLE_A) that automatically increments it's id upon each new insertion (i.e. the first record in the database has it's ID attribute 1, the second record has it's ID attribute set to 2, the third record has it's ID attribute set to 3). The ID I am talking about is the table's primary key.
I also have another table (MY_TABLE_B) that reference's the original table's primary key. When I try to persist both to my Oracle database, I get a org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()
What I want to accomplish: Whenever I persist an object to MY_TABLE_A, I want MY_TABLE_B to insert an object with the same ID that MY_TABLE_A gets since it's auto incremented (wouldn't know what the next value is until it's inserted). To clarify, one id in Table A should have only one matching ID in Table B
Here are some snippets of my code below:
FirstClass:
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_A")
#Component
public class FirstClass implements Serializable {
#Id
#SequenceGenerator(name = "MY_SEQ", sequenceName = "MY_SCHEMA.MY_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQ")
#Column(name = "MY_ID")
private Integer myId;
// more variables, getters/setters
}
SecondClass:
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_B")
#SecondaryTable(name = "MY_SCHEMA.MY_TABLE_A", pkJoinColumns = #PrimaryKeyJoinColumn(name = "MY_ID", referencedColumnName = "MY_ID"))
#Component
public class SecondClass {
#Id
#Column(name = "MY_ID")
private Integer myId;
// more variables, getters/setters
}
Service Layer snippet where I insert new entries for each in Oracle:
firstClassService.insert();
secondClassService.insert();
Details on insert() for firstClassService:
public void insert() {
FirstClass obj = new FirstClass();
getCurrentSession().persist(obj);
}
insert() for secondClassService:
public void insert() {
SecondClass obj = new SecondClass();
getCurrentSession().persist(obj);
}
UPDATE
What FirstClass looks like now:
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_A")
#Component
public class FirstClass implements Serializable {
#Id
#SequenceGenerator(name = "MY_SEQ", sequenceName = "MY_SCHEMA.MY_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQ")
#Column(name = "MY_ID")
#OneToOne(mappedBy = "myId")
private Integer myId;
}
SecondClass:
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_B")
#SecondaryTable(name = "MY_SCHEMA.MY_TABLE_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "MY_ID", referencedColumnName = "MY_ID"))
#Component
public class SecondClass implements Serializable {
#Id
#JoinColumn(name = "MY_ID", referencedColumnName = "MY_ID")
#OneToOne
private Integer restRequestId;
}
Mappings should be as below:
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_A")
#Component
public class FirstClass implements Serializable {
#Id
#SequenceGenerator(name = "MY_SEQ", sequenceName = "MY_SCHEMA.MY_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQ")
#Column(name = "MY_ID")
private Long myId;
#OneToOne(mappedBy = "firstClass", cascade = CascadeType.ALL)
private SecondClass secondClass;
}
#Entity
#Table(name = "MY_SCHEMA.MY_TABLE_B")
#Component
public class SecondClass implements Serializable {
#Id
#JoinColumn(name = "MY_ID", referencedColumnName = "MY_ID")
#OneToOne
private FirstClass firstClass;
}
With the Cascade option set then you you will only need to make the call to save firstClass: the associated secondClass will be persisted automatically - assuming you set both sides of the relationhsip in your in-memory model i.e.
firstClass.setSecondClass(secondClass);
secondClass.setFirstClass(firstClass);
Add #GeneratedValue(strategy=GenerationType.IDENTITY) to the id of second class.
#Id
#Column(name = "MY_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer myId;
// more variables, getters/setters
From your description it seems like you have a ManytoOne relation, as your table B references table A, then it's logic to say A has a list of Bs somewhat, so why not take advantage of what ORM actually is and why not keep a reference in A such as:
#OneToMany(mappedBy="aa")
private List<B> bs;
and use the annotation in the other entity:
#ManyToOne
#JoinColumn(name = "myId" , referencedColumnName = "id")
private A aa;
That in combination to what Jens suggested, see OracleDialect does not support identity key generation

Hibernate criteria. Select by alias

I have the table
#Entity
#Table(name="cash")
public class CashItem extends Item {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "cash_seq_gen")
#SequenceGenerator(name = "cash_seq_gen", sequenceName = "cash_seq")
private Long id;
#Column(name="object_id")
private Long objectId = 1l;
#Column(name="object_type")
private Long objectType = 1l;
And the link table
#Entity
#Table(name="cash_to_order")
#SuppressWarnings("serial")
public class CashToOrderItem extends Item {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "cash_to_order_seq_gen")
#SequenceGenerator(name = "cash_to_order_seq_gen", sequenceName = "cash_to_order_seq")
Long id;
#ManyToOne
#JoinColumn(name="order_id",referencedColumnName="id")
private OrderItem order;
#ManyToOne
#JoinColumn(name="cash_id",referencedColumnName="id")
private CashItem cash;
I get Criteria from CashToOrderItem and wanna make next query
criteria.createAlias("cash","c1");
criteria.add(Restrictions.eq("c1.objectType",ocf.getCounterparty()));
criteria.add(Restrictions.eq("с1.objectId",ocf.getSubCounterparty()));
But error occured what could not resolve property: с1 of: CashToOrderItem if i added two or more Restrictions.
If you are creating criteria on CashToOrder, please reach out to cash from that class for alias. i.e. cashToOrder.cash
Criteria criteria = session.createCriteria(CashToOrderItem.class, "cashToOrder");
criteria.createAlias("cashToOrder.cash","c1");

Categories

Resources