Get container entity using child id - jpa

I want to get the branch object in which leaf(using it's ID) belongs to
What is the right approach to get the branch given that I only have the leaf ID? I thought of looping through all the branches in the db and get the one which contain the leaf ID which looks bad
#Entity
public class Branch {
#Id
#GeneratedValue
Long id;
#OneToMany
#JoinColumn(name = "branch_id")
private List<Leaf> leaves
}
#Entity
public class Leaf {
#Id
#GeneratedValue
Long id;
private String name;
}
#Service
public class BranchService {
private final BranchRepository branchRepository;
#Autowired
public BranchService(BranchRepository branchRepository) {
branchRepository = branchRepository;
}
public Tree getBranchByLeaf(Long leafId){
// ??
}
}

Try something like this:
public interface BranchRepository extends JpaRepository<Branch, Long> {
#Query("select b from Branch b join b.leaves l where l.id = ?1")
List<Branch> getByLeafId(Long leafId);
}
#Service
public class BranchService {
private final BranchRepository branchRepository;
#Autowired
public BranchService(BranchRepository branchRepository) {
branchRepository = branchRepository;
}
public List<Branch> getByLeafId(Long leafId){
return branchRepository.getByLeafId(Long leafId);
}
}

Related

There is no ID defined for this entity hierarchy

I am stuck with this error message, that appears every time I want to add a ManytoOne relationship with another entity class.
The class must use a consistent access type (either field or property). There is no ID defined for this entity hierarchy
This is my entity Transaction
#Entity
#Table(name = "CustomerTransaction")
public class CustomerTransaction implements Serializable {//this is the line with the error message
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne //This generates the problem
#JoinColumns({
#JoinColumn(name = "CUS_ID", referencedColumnName = "IDCUSTOMER") })
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private long transactionID;
#Temporal(TemporalType.TIMESTAMP)
private Date buyDate;
public Date getBuyDate() {
return buyDate;
}
public void setBuyDate(Date buyDate) {
this.buyDate = buyDate;
}
public long getTransactionID() {
return transactionID;
}
public void setTransactionID(long transactionID) {
this.transactionID = transactionID;
}
public String getCarYear() {
return carYear;
}
public void setCarYear(String carYear) {
this.carYear = carYear;
}
public Date getTransactionDate() {
return transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
private String carYear;
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate;
JPA annotation should all be placed either on fields or on accessor methods. You've placed the #Id and #GeneratedValue annotation on a field (private Long id), but #ManyToOne and #JoinColumns on a getter (public Long getId()). Move the latter on a field as well.
i had similar error but in the end, i realized #Id was referencing this package org.springframework.data.annotation.Id instead of javax.persistence.Id. i was using #MappedSuperClass approach so as soon as i corrected this, everything worked fine
You need to import #Id from "import javax.persistence.Id;"

JPA how to group by a collection property

Is there a way to group by a collection property? For example,
public class Merchandise {
id,
name
}
public class Attribute {
id,
name,
value,
#ManyToOne
MerchandiseCost merchandiseCost;
}
public class MerchandiseCost {
Merchandise merchandise,
List<Attribute> attributes,
BigDecimal cost,
}
Search MerchandiseCost group by merchandise and attributes.
select merchandise, attributes, sum(cost) from MerchandiseCost group by merchandise, attributes.
Will this be going to work?
EDIT:
If not, how to build a query to get results as following using CriteriaQuery API:
Merchandise Attributes SUM(COST)
-----------------------------------------------------------
Cloth size:L, color:RED 10000
Cloth size:M, color:WHITE 20000
Computer Memory:4G 80000
Computer Memory:16G 90000
You can not group by a collection and cannot select multi-valued field in the Select clause.
Merchandise.class
#Data
#Embeddable
#NoArgsConstructor
#EqualsAndHashCode
public class Merchandise {
private String name;
}
Attribute.class
#Data
#Embeddable
#EqualsAndHashCode
public class Attribute {
private int id;
private String name;
private String value;
private MerchandiseCost merchandiseCost;
#ManyToOne
public MerchandiseCost getMerchandiseCost() {
return merchandiseCost;
}
}
MerchandiseCost.class
#Data
#Entity
#EqualsAndHashCode
public class MerchandiseCost extends ABaseEntity {
private Merchandise merchandise;
private List<Attribute> attributes;
private BigDecimal cost;
#Embedded
public Merchandise getMerchandise() {
return merchandise;
}
public void setMerchandise(Merchandise merchandise) {
this.merchandise = merchandise;
}
#ElementCollection
#CollectionTable(name = "MERCHANDISE_ATTRIBUTE", joinColumns = #JoinColumn(name = "MERCHANDISE_ID"))
public List<Attribute> getAttributes() {
return attributes;
}
}
MerchandiseResult.class
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class MerchandiseResult {
private Merchandise merchandise;
private Attribute attribute;
private BigDecimal cost;
}
MerchandiseDao.class
#Stateless
public class MerchandiseDao {
#PersistenceContext(name = "tngo")
private EntityManager entityManager;
public void readCost(){
Query query = entityManager.createQuery("select NEW tngo.cert.training.model.MerchandiseResult(mc.merchandise, att, sum(mc.cost)) from MerchandiseCost mc join mc.attributes att group by mc.merchandise, att");
query.getResultList();
}
}

Disable additional criteria only in some entity relations

I am making an application based on JPA/EclipseLink, I implemented a soft delete functionality using #AdditionalCriteria in the root class of the hierarchy of entities (all entities inherit from this).
My problem is that now, I need to create a special entity that contains multiple relationships with other entities; and I need recover all relationed entities, including soft deleted ones. It is possible disabled #AdditionalCriteria only in the relations of this special entity with EclipseLink? If not, what is the best option to do this? My code looks like the following:
////Example of my top entity class (all others inherit from this)
#AdditionalCriteria("this.deleted = false")
public abstract class TopHierarchyClass {
···
#Column(name = "deleted")
protected boolean deleted = false;
···
}
//Example of entity that needs recover all relationed entities including soft deleted
#Entity
#Table(name = "special_entities")
public class SpecialEntity extends EntityBase {
···
#JoinColumn(name = "iditem", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Item item;
···
}
#Entity
#Table(name = "items")
public class Item extends EntityBase {
···
}
Thanks in advance
Create a new entity for the same table without the #AdditionalCriteria. This way you can retrieve all records from that table without applying the additional filter.
For example:
public interface Person {
Long getId();
void setId(Long id);
Date getDeletedAt();
void setDeletedAt(Date deletedAt);
}
#MappedSupperclass
public class PersonEntity implements Person {
#Id
private Long id;
private Date deletedAt;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Date getDeletedAt() { return deletedAt; }
public void setDeletedAt(Date deletedAt) { this.deletedAt = deletedAt; }
}
#Entity
#Table(name = "people")
#AdditionalCriteria("this.deletedAt is null")
public class ActivePersonEntity extends PersonEntity {
}
#Entity
#Table(name = "people")
public class RawPersonEntity extends PersonEntity {
}
public class PeopleRepository {
#PersistenceContext
private EntityManager em;
public List<Person> all() {
return all(false);
}
public List<Person> all(boolean includeSoftDeleted) {
if (!includeSoftDeleted) {
return em.createQuery("select p from ActivePersonEntity p", ActivePersonEntity.class).getResultList();
} else {
return em.createQuery("select p from RawPersonEntity p", RawPersonEntity.class).getResultList();
}
}
}
Also, if your #AdditionalCriteria is in a super class, you may override it by declaring a new empty #AdditionalCriteria in a sub class:
You can define additional criteria on entities or mapped superclass.
When specified at the mapped superclass level, the additional criteria
definition applies to all inheriting entities, unless those entities
define their own additional criteria, in which case those defined for
the mapped superclass are ignored.
#AdditionalCriteria doc

JPA Mapping, parent to children and child to parent with two classes and abstract class

I have have two classes that inherit from an abstract class and have a parent-children relation.
So I use annotation OneToMany and ManyToOne but the parent entity in child class is always null.
Can Someone help me please, I have spend several hours to googling and test many conf without success.
These are code from my classes :
public #Table(name="flowentity") #Entity abstract class FlowEntity {
final static Logger log = LoggerFactory.getLogger(FlowEntity.class);
//Globals informations concerning the flow state
private #Id #GeneratedValue(strategy = GenerationType.IDENTITY) Integer flowId = 0;
private String flowName;
private #OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
Set<PeopleEntity> actorSet = new HashSet<>();
//Global parameters for most of flows
//Organizational parameters
private #OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="organisationalEntity_Id")
OrganisationalEntity organisationalEntity;
...
public #Table(name="ams_newCPEntity") #Entity class NewMultiCPEntity extends FlowEntity {
private #OneToMany(targetEntity=NewCPEntity.class, fetch=FetchType.EAGER, cascade=CascadeType.ALL,mappedBy="parent")
Set<NewCPEntity> cpList = new HashSet<NewCPEntity>();
//Constructor
public NewMultiCPEntity(){
setFlowName(EnumFlow.N_CP_M.getFlowAcronym());
}
...
public #Table(name="ams_newCPEntity") #Entity class NewCPEntity extends FlowEntity {
final static Logger log = LoggerFactory.getLogger(NewCPEntity.class);
private boolean formNCPValidated;
private #ManyToOne #JoinColumn(name="parent_Id", nullable=false)
NewMultiCPEntity parent;
public NewCPEntity(){
log.debug("Instanciation of a new CP");
setFlowName(EnumFlow.N_CP.getFlowAcronym());
}
public #Override OrganisationalEntity getOrganisationalEntity(){
return parent.getOrganisationalEntity();
}
...
If I don't add the #JoinColumn annotation, JPA create an association table but is not able to retrieve the parent whereas the association can be done directly by requesting in database.
Thankyou very much to help.
Regards,
Thank you Chris for your comment, you are right, I forget to change the name of the table. I don't think it was the problem because the inheritance mapping is in one table flowentity with a DTYPE discriminator column.
Finally I resolve my problem by setting parent attributs when adding a new child like this :
public #Table #Entity class NewMultiCPEntity extends FlowEntity {
private #OneToMany(targetEntity=NewCPEntity.class, fetch=FetchType.EAGER, cascade=CascadeType.ALL)
List<NewCPEntity> cpList = new ArrayList<>();
//Constructor
public NewMultiCPEntity(){
setOrganisationalEntity(new OrganisationalEntity());
setFlowName(EnumFlow.N_CP_M.getFlowAcronym());
}
public List<NewCPEntity> getNCPList(){
if(cpList == null){
cpList = new ArrayList<>();
}
if(cpList.isEmpty()){
addCPEntity(new NewCPEntity());
}
return Collections.unmodifiableList(cpList);}
public boolean removeCPEntity(NewCPEntity entity){
return cpList.remove(entity);
}
public boolean addCPEntity(NewCPEntity entity){
entity.setParent(this);
entity.setOrganisationalEntity(this.getOrganisationalEntity());
return cpList.add(entity);
}
And I remove the override of getOrganizationalEntity in the child :
public #Table #Entity class NewCPEntity extends FlowEntity {
final static Logger log = LoggerFactory.getLogger(NewCPEntity.class);
private #ManyToOne(targetEntity=NewMultiCPEntity.class,cascade=CascadeType.ALL)
NewMultiCPEntity parent;
public NewCPEntity(){
log.debug("Instanciation of a new CP");
setFlowName(EnumFlow.N_CP.getFlowAcronym());
}
public NewMultiCPEntity getParent() {
return parent;
}
public void setParent(NewMultiCPEntity parent){
this.parent = parent;
}
Regards,

not quite understanding lazy-loading

I have a situation where lazy-loading is not occurring when I want it to in a one-to-many.
Obviously, I am new to this. Why won't the SongComposers get fetched when I call s.getSongComposers()?
Here is my DAO class:
public class SongDAOImpl implements SongDAO {
#PersistenceContext
private EntityManager em;
#Override
#SuppressWarnings(value = { "unchecked" })
public List<Song> getAllSongsOnAlbum(int albumID) {
Query q = em.createQuery("SELECT s FROM Song s WHERE s.album.albumID = :albumid ORDER BY s.songName");
q.setParameter("albumid", albumID);
List<Song> list = (List<Song>) q.getResultList();
for (Song s: list) {
s.getSongComposers();
}
return list;
}
}
Here is my Song entity:
#Entity
#Table(name="songs")
public class Song {
#Id
#GeneratedValue
#Column(name="SongID")
private int songID;
#Column(name="SongName")
private String songName;
#Column(name="Length")
private int length;
#ManyToOne
#JoinColumn(name="AlbumID", referencedColumnName="AlbumID")
private Album album;
#OneToMany(mappedBy="song")
private List<SongComposer> songComposers;
.... all getters/setters ......
}
Here is my SongComposer entity:
#Entity
#Table(name="song_composer")
public class SongComposer {
#Id
#GeneratedValue
#Column(name="SongComposerID")
private int songComposerID;
#ManyToOne
#JoinColumn(name="SongID", referencedColumnName="SongID")
private Song song;
#ManyToOne
#JoinColumn(name="ComposerID", referencedColumnName="ComposerID")
private Composer composer;
..... getters/setters .......
}
You have to do it explicitly. Try this query:
SELECT DISTINCT s FROM Song s JOIN FETCH s.songComposers WHERE s.album.albumID = :albumid ORDER BY s.songName"