I've a bidirectional relationship between two entities: Item (15 records) and maintenanceContract (22 records)
Mapping table item_maintenanceContract linking these two entities contains 25 records
I would like to count the records in the maintenanceContract table and I run the following query:
"SELECT count(*) FROM MaintenanceContract mc LEFT JOIN mc.items as i "
Unfortunately I visualize 25 instead of the desidered 22.
I cannot simply count on MaintenanceContract because join with Item is required when i perform same searches based on item attributes.
I tried alternative combinations: LEFT JOIN , left outer JOIN, right JOIN... no changes in the result
What I'm missing?
My item definition:
#Entity
#Table(name = "item"})
#Inheritance(strategy = InheritanceType.JOINED)
public class Item extends BaseEntity implements Serializable {
private Long id;
private Set<MaintenanceContract> maintenanceContracts = new HashSet<MaintenanceContract>(0);
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "item_maintenancecontract", joinColumns = {
#JoinColumn(name = "item_id", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "maintenancecontract_id",
nullable = false, updatable = false)})
public Set<MaintenanceContract> getMaintenanceContracts() {
return maintenanceContracts;
}
public void setMaintenanceContracts(Set<MaintenanceContract> maintenanceContracts) {
this.maintenanceContracts = maintenanceContracts;
}
}
and my MaintenanceContract
#Entity
public class MaintenanceContract implements Serializable {
private Long id;
private Set<Item> items = new HashSet<Item>(0);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "maintenanceContracts")
public Set<Item> getItems() {
return this.items;
}
public void setItems(Set<Item> items) {
this.items = items;
}
}
Related
I have two enteties and I want to be able access one of them from another and vise versa (bidirectional). But sometimes when persisting an order as stopLossOrder it's not saved to position. If you have any ideas of how it can be implemented (if it's possible this way or similar) I would be glad to hear.
Later on I want to add more orders to position entity similarly to 'stopLossOrder'
#Entity(name = "Position")
#Table(name = "positions")
#Getter #Setter
public class PositionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true)
#JoinColumn(name = "stop_loss_order_id", referencedColumnName = "id")
private OrderEntity stopLossOrder;
#OneToMany(mappedBy = "position",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<OrderEntity> orders = new HashSet<>();
public void setStopLossOrder(OrderEntity stopLossOrder) {
this.stopLossOrder = stopLossOrder;
stopLossOrder.setPosition(this);
}
public boolean addOrder(OrderEntity orderEntity) {
orderEntity.setPosition(this);
return orders.add(orderEntity);
}
}
#Entity(name = "Order")
#Table(name = "orders")
#Getter
#Setter
public class OrderEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = -1462587657644552577L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(optional = false)
private PositionEntity position;
}
Persisting:
if (orderEntity.getType() == OrderType.STOP_MARKET) {
positionEntity.setStopLossOrder(orderEntity);
} else {
boolean isAdded = positionEntity.addOrder(orderEntity);
if (!isAdded)
throw new TradeServiceException("Order with id: " + order.getOrderId() + " already added to position.");
}
orderEntity = orderRepository.save(orderEntity);
I have a mysql table which maintains data of drivers and maintains the city of the driver by using the foreign key mapping.
public class Drivers {
private Integer currentCityId;
private Integer id;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "current_city_id")
public Integer getCurrentCityId() {
return currentCityId;
}
public void setCurrentCityId(Integer currentCityId) {
this.currentCityId = currentCityId;
}
#ManyToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "current_city_id", insertable = false, updatable = false, nullable = true, unique = false)
public Cities getCities() {
return cities;
}
public void setCities(Cities cities) {
this.cities = cities;
}
}
#Entity
#Table(name = "cities", catalog = "mytable_production", uniqueConstraints = #UniqueConstraint(columnNames = "name"))
public class Cities implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private String aliasName1;
private String aliasName2;
private int stateId;
}
Now, I am updating the city of the driver by updating the foreignKey value in the table using
#Transactional
public void updateCityBizLogic(int driverId,int newCityId) {
//Some biz logic
Drivers d = driversDao.updateCity(driverId,newCityId);
log.info("Updated driverCity to {}",d.getCities.getName());
}
public class DriversDao {
#Transactional
public Drivers updateCity(int DriverId, int newCityId) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Drivers.class);
criteria.add(Restrictions.eq("id", Integer.parseInt(id)));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
Drivers d = criteria.list().get(0);
d.setCurrentCityId(newCityId);
session.update(d);
return d;
}
}
But in the log line, it is printing the old city name. I want the session to update the associated entities when I update any of the foreign key ( like update the joined cities object, when I update the cityId)
Can someone point out what I am missing here and achieve it?
I have entities as below. I need to retrieve list of CIDs from CEntity using AEntity's id;
I have to traverse thru AEntity -> ABMapping -> BEntity -> fetch CID from CEntity.
Is there a way to achieve this in JPA or Should I go native query way joining all four tables and get CIDs from CEntity?
Entity A
#Entity
public class AEntity {
#Id
private long id;
#ManyToMany
#JoinTable(name = "ABMapping", joinColumns = #JoinColumn(name = "AEntity_ref", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "BEntity_ref", referencedColumnName = "id"))
private List<BEntity> bEntities = new ArrayList<>();
}
Entity B
#Entity
public class BEntity {
#Id
private long id;
private CEntity cEntity;
#ManyToMany(mappedBy = "bEntities")
private List<AEntity> aEntities;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cEntityId")
public CEntity getCEntity() {
return cEntity;
}
}
Entity ABMapping
#Entity
public class ABMapping {
#Id
private long id;
#Column(name="AEntity_ref")
private long ARefId;
#Column(name = "BEntity_ref")
private long BRefId;
}
Entity C
#Entity
public class CEntity {
#Id
private long id;
private String CID;
private List<BEntity> bEntity;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "c", cascade =
CascadeType.ALL)
public List<BEntity> getBEntities() {
return bEntity;
}
#Column(name = "CID_column")
public String getCId() {
return CID;
}
public void setCId(String CID) {
this.CID = CID;
}
}
I went with what #JB Nizet has suggested with.
select distinct c from AEntity a join a.bEntities b join b.cEntity c where a.id = :id
I am trying to understand and figure out the solution for the following use case
These are my entity classes
User
#Entity
#Table(name = "USER")
public class User {
private UserID id;
private Set<UserAddress> addresses = new HashSet<UserAddress>(0);
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name = "userId", column = #Column(name = "USER_ID", nullable = false, length = 32)),
#AttributeOverride(name = "userType", column = #Column(name = "USER_TYPE", nullable = false, precision = 12, scale = 0)) })
public User getId() {
return this.id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user", cascade={CascadeType.ALL})
#BatchSize(size=50)
public Set<UserAddress> getAddresses() {
return this.addresses;
}
........
}
UserAddress
#Entity
#Table(name = "USERADDRESS")
public class UserAddress {
private UserID id;
Private User user;
private String address;
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name = "userId", column = #Column(name = "USER_ID", nullable = false, length = 32)),
#AttributeOverride(name = "userType", column = #Column(name = "USER_TYPE", nullable = false, precision = 12, scale = 0)) })
public User getId() {
return this.id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns( {
#JoinColumn(name = "userId", referencedColumnName = "USER_ID", nullable = false, insertable=false, updatable=false),
#JoinColumn(name = "userType", referencedColumnName = "USER_TYPE", nullable = false, insertable=false, updatable=false) })
public User getUser() {
return this.user;
}
........
}
UserId
#Embeddable
public class UserId implements Serializable {
private String userNo;
private Long UserType;
.......
.......
}
I have created a staticmetamodel class for User, UserID and UserAddress and created query based on Specifications.
Metamodel class for User
#StaticMetamodel(User.class)
public abstract class User_ {
public static volatile SetAttribute<User, UserAddress> addresses;
public static volatile SingularAttribute<User, UserID> id;
}
Metamodel for UserId
#StaticMetamodel(UserID.class)
public abstract class UserID_ {
public static volatile SingularAttribute<UserID, String> userNo;
public static volatile SingularAttribute<UserID, Long> UserType;
}
I am trying to retrieve maximum of 10 User objects ordered by UserType and searched based on userId. The query has to retrieve the UserAddresses as an eager fetch.
My Specification Object is
UserSpecification
public class UserSpecifications {
public static Specification<User> userNoIs(String userNo) {
return (root, query, cb) -> {
root.fetch(User_.addresses);
return cb.equal(root.get(User_.id).get(UserID_.userNo),userNo);
};
}
}
DAO Function:
Sort sortInstructions = new Sort(Sort.Direction.DESC, "id.userNo");
Specifications<User> specifications = Specifications.where(userNoIs(input.getUserNo()));
List<User> userList = userRepository.findAll(specifications,sortInstructions);
I am getting the following exception
java.lang.ClassCastException: org.hibernate.jpa.internal.metamodel.SingularAttributeImpl$Identifier cannot be cast to javax.persistence.metamodel.ManagedType
at org.hibernate.jpa.criteria.path.AbstractFromImpl.locateManagedType(AbstractFromImpl.java:139)
at org.hibernate.jpa.criteria.path.AbstractFromImpl.locateAttributeInternal(AbstractFromImpl.java:133)
at org.hibernate.jpa.criteria.path.AbstractPathImpl.locateAttribute(AbstractPathImpl.java:221)
at org.hibernate.jpa.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:194)
at org.springframework.data.jpa.repository.query.QueryUtils.toJpaOrder(QueryUtils.java:287)
at org.springframework.data.jpa.repository.query.QueryUtils.toOrders(QueryUtils.java:261)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:441)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:322)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:307)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
The problem is with root.fetch(User_.userAddresses); - in you User class you have addresses not userAddresses so probably you wrongly generated metamodel for that.
Another thing that addresses should be ListAttribute not SingularAttribute.
UPDATE EDIT:
Try to use newer version, problem you described was solved in spring-data 1.5
I have 2 tables I want to join on PK and FK in JPA, but the PK is upper case and the FK lower case.
How do I map case insensitive association between Person -> GroupAssociationEntity?
My current mapping is not working.
#Entity
public class Person {
#Id
#Column(name = "id", columnDefinition = "nvarchar")
private String id;
#OneToMany(mappedBy = "person")
private List<GroupAssociationEntity> groups;
}
#Entity
#IdClass(GroupAssociationKey.class)
public class GroupAssociationEntity {
#Id
private String id;
#Id
private String memberOf;
#ManyToOne
#JoinColumn(name = "id", updatable = false, insertable = false, referencedColumnName = "id")
private Group group;
#ManyToOne
#JoinColumn(name = "memberOf", updatable = false, insertable = false, referencedColumnName = "id")
private Person person;
....
}
#Entity
public class Group {
#Id
#Column(name = "id")
private String id;
#OneToMany(mappedBy = "group")
private List<GroupAssociationEntity> persons;
......
}
I switched your mapping to:
#Entity(name = "Person")
public static class Person {
#Id
#Column(name = "id")
private String id;
#OneToMany(mappedBy = "person")
private List<GroupAssociationEntity> groups;
}
#Entity(name = "GroupAssociationEntity")
public static class GroupAssociationEntity {
#EmbeddedId
private GroupAssociationKey id;
#ManyToOne
#MapsId("id")
private Group group;
#ManyToOne
#MapsId("memberOf")
private Person person;
}
#Embeddable
public static class GroupAssociationKey implements Serializable{
private String id;
private String memberOf;
public GroupAssociationKey() {
}
public GroupAssociationKey(String id, String memberOf) {
this.id = id;
this.memberOf = memberOf;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMemberOf() {
return memberOf;
}
public void setMemberOf(String memberOf) {
this.memberOf = memberOf;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof GroupAssociationKey)) return false;
GroupAssociationKey that = (GroupAssociationKey) o;
return Objects.equals(getId(), that.getId()) &&
Objects.equals(getMemberOf(), that.getMemberOf());
}
#Override
public int hashCode() {
return Objects.hash(getId(), getMemberOf());
}
}
#Entity(name = "Group")
#Table(name = "groups")
public static class Group {
#Id
#Column(name = "id")
private String id;
#OneToMany(mappedBy = "group")
private List<GroupAssociationEntity> persons;
}
And run this test on both SQL Server and MySQL:
doInJPA( entityManager -> {
Person person1 = new Person();
person1.id = "abc1";
entityManager.persist(person1);
Person person2 = new Person();
person2.id = "abc2";
entityManager.persist(person2);
Group group = new Group();
group.id = "g1";
entityManager.persist(group);
GroupAssociationEntity p1g1 = new GroupAssociationEntity();
p1g1.id = new GroupAssociationKey("G1", "ABC1");
p1g1.group = group;
p1g1.person = person1;
entityManager.persist(p1g1);
GroupAssociationEntity p2g1 = new GroupAssociationEntity();
p2g1.id = new GroupAssociationKey( "G1", "ABC2" );
p2g1.group = group;
p2g1.person = person2;
entityManager.persist(p2g1);
} );
doInJPA( entityManager -> {
Group group = entityManager.find(Group.class, "g1");
assertEquals(2, group.persons.size());
} );
doInJPA( entityManager -> {
Person person = entityManager.find(Person.class, "abc1");
assertEquals(1, person.groups.size());
} );
And it works just fine. Check it out on GitHub.
#Entity
public class Person {
#Id
#Column(name = "id", columnDefinition = "nvarchar")
private String id;
}
#Entity
#IdClass(GroupAssociationKey.class)
public class GroupAssociationEntity {
#Id
private String id;
#Id
private String memberOf;
#ManyToOne
#JoinColumn(name = "id", updatable = false, insertable = false, referencedColumnName = "id")
private Group group;
#ManyToOne
#JoinColumn(name = "id", updatable = false, insertable = false, referencedColumnName = "id")
private Person person;
....
}
#Entity
public class Group {
#Id
#Column(name = "id")
private String id;
}
try this.