Hibernate Postgresql select for update with outer join issue - postgresql

I have faced with issue trying to select for update row using Spring data with Hibernate as JPA implementation and Postgresql.
Suppose we have entities:A,B,C.
public class A{
#Id
private Long id;
#OneToMany(fetch = FetchType.EAGER)
private Set<B> bSet;
#OneToMany(fetch = FetchType.EAGER)
private Set<C> cSet;
}
Suppose we want to select A with all related B and C entities for update i.e. with locking row related to A table.
#Query(SELECT a FROM A a
LEFT JOIN FETCH a.bSet
LEFT JOIN FETCH a.cSet
WHERE a.id=?)
#Lock(LockModeType.PESSIMISTIC_WRITE)
public A selectAndLockA(Long Aid);
The query will look like
SELECT a.column1, ... from tableA a LEFT JOIN tableB b ... FOR UPDATE of a,c
FOR UPDATE of a,c
The query will try to lock two tables what leads to exception like :
org.postgresql.util.PSQLException: ERROR: FOR UPDATE cannot be applied to the nullable side of an outer join
What I try to archive is locking only first table "FOR UPDATE OF a"
Is it possible to configure somehow or tell Hibernate to lock only first table.

This is not supported by PostreSQL. If you do an outer SELECT nothing can prevent somebody from inserting a row into the LEFT JOINED table thereby modifiying the result set you are looking at (e.g. the columns would not be NULL anymore on a repeated read).
For a detailed explanantion see here

It's been a long time since question was created, but I have a similar problem and hope my answer will help somebody.
Suppose that we have this JPA entities:
#Entity
#Table(name = "card_transactions")
public class CardTransactionsEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "card_trans_seq")
#SequenceGenerator(name = "card_trans_seq", sequenceName = "card_trans_seq")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ofd_id", referencedColumnName = "ofd_id"),
#JoinColumn(name = "receipt_id", referencedColumnName = "receipt_id")})
private ReceiptsEntity receipt;
#Column
#Enumerated(EnumType.STRING)
private CardTransactionStatus requestStatus;
...
}
#Entity
#Table(name = "receipts")
public class ReceiptsEntity {
#EmbeddedId
private OfdReceiptId id;
...
}
#Embeddable
public class OfdReceiptId implements Serializable {
#Column(name = "ofd_id")
#Enumerated(EnumType.STRING)
private OfdId ofdId;
#Column(name = "receipt_id")
private String receiptId;
...
}
And we want select CardTransactionsEntity with fetched ReceiptsEntity for pessimistic update only CardTransactionsEntity. This can be done using Hibernate and Spring Data JPA repository as
public interface CardTransactionRepository extends JpaRepository<CardTransactionsEntity, Long> {
#Query("select ct from CardTransactionsEntity ct left join fetch ct.receipt r where ct.requestStatus = :requestStatus")
#Lock(value = LockModeType.PESSIMISTIC_WRITE)
#QueryHints(value = {
#QueryHint(name = "javax.persistence.lock.timeout", value = "-2"), // LockOptions.SKIP_LOCKED
#QueryHint(name = "org.hibernate.lockMode.r", value = "NONE") // "r" is alias for ct.receipt and will excluded from PESSIMISTIC_WRITE
})
List<CardTransactionsEntity> loadCardTransactions(#Param("requestStatus") CardTransactionStatus requestStatus, Pageable pageable);
}
This repository method will execute query like
SELECT ct.*, r.* from card_transactions ct LEFT OUTER JOIN receipts r ON ct.ofd_id = r.ofd_id and ct.receipt_id = r.receipt_id WHERE ct.request_status=? LIMIT ? FOR UPDATE OF ct SKIP LOCKED

You can bypass this error with joining the tables with FetchType.LAZY. This fetch type is the default one and it is not required to specify for #OneToMany joins.
public class A{
#Id
private Long id;
#OneToMany
private Set<B> bSet;
#OneToMany
private Set<C> cSet;
}

Related

spring data error when trying to sort by a field of joined entity inside a crudrepository

I am using springboot and springdata with Mysql.
I have 2 entities, Customer & Order:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name="customer_id")
private long customerId;
}
I also have a repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Long> {
#Query("select o from Order o, Customer c where o.customerId = c.id")
Page<Order> searchOrders(final Pageable pageable);
}
The method has some more arguments for searching, but the problem is when I send a PageRequest object with sort that is a property of Customer.
e.g.
Sort sort = new Sort(Sort.Direction.ASC, "c.name");
ordersRepository.search(new PageRequest(x, y, sort));
However, sorting by a field of Order works well:
Sort sort = new Sort(Sort.Direction.ASC, "id");
ordersRepository.search(new PageRequest(x, y, sort));
The error I get is that c is not a property of Order (but since the query is a join of the entities I would expect it to work).
Caused by: org.hibernate.QueryException: could not resolve property c of Order
Do you have any idea how I can sort by a field of the joined entity?
Thank you
In JPA , the thing that you sort with must be something that is returned in the select statement, you can't sort with a property that is not returned
You got the error because the relationship is not modeled properly. In your case it is a ManyToOne relation. I can recomend the wikibooks to read further.
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#ManyToOne
#JoinColumn(name="customer_id", referencedColumnName = "id")
private Customer customer;
}
The query is not needed anymore because the customer will be fetched.
#Repository
public interface OrdersRepository extends PagingAndSortingRepository<Order, Long> {
}
Now you can use nested properties.
Sort sort = new Sort(Sort.Direction.ASC, "customer.name");
ordersRepository.findAll(new PageRequest(x, y, sort));

JPA NamedQuery with Join returning empty list

I've been struggling with a namedquery for a few days. The named query has an inner join to a 2nd table. One added complexity is that the primary key on the 2nd table is a composite key. I have the two tables simplified here:
Table: aname
nameIdx number(9),
firstName varchar2(40),
lastName varchar2(40),
primary key is nameIdx
Table: aname_role
nameIdx number(9), --foreign key to name table
nameType char(2),
inactiveFlag char(1)
composite primary key is on nameIdx and nameType
I am trying to emulate the following sql query in JPQL:
select * from aname n
left join aname_role nr on n.nameidx=nr.nameidx
where nr.nametype='5'
and nr.inactiveflag='N';
This query works as expected in Oracle returning many records. In Java I have these JPA entities:
#Entity
#Table(name="ANAME")
#NamedQueries({
#NamedQuery(name = "AName.findActiveSalesPersons", query = "SELECT a FROM AName a LEFT JOIN a.aNameRoleList r WHERE r.inactiveflag='N' and r.ANameRolePK.nametype='5' ")})
public class AName implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "NAMEIDX")
private Integer nameidx;
#Column(name = "FIRSTNAME")
private String firstname;
#Column(name = "LASTNAME")
private String lastname;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "aName")
private List<ANameRole> aNameRoleList;
//getters and setters here
and
#Entity
#Table(name = "ANAME_ROLE")
public class ANameRole implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected ANameRolePK aNameRolePK;
#Basic(optional = false)
#NotNull
#Column(name = "INACTIVEFLAG")
private Character inactiveflag;
#JoinColumn(name = "NAMEIDX", referencedColumnName = "NAMEIDX", insertable = false, updatable = false)
#ManyToOne(optional = false)
private AName aName;
//getters and setters here
There is also a primary key class ANameRolePK
#Embeddable
public class ANameRolePK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "NAMEIDX")
private int nameidx;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 2)
#Column(name = "NAMETYPE")
private String nametype;
//getters and setters here
With this setup, including the named query specified in the AName entity above, the following returns an empty result list:
em.createNamedQuery("AName.findActiveSalesPersons").getResultList();
Can anyone point me to what I am doing wrong in this named query?
SELECT a FROM AName a LEFT JOIN a.aNameRoleList r WHERE r.inactiveflag='N' and r.aNameRolePK.nametype='5'
Thanks,
Steve
By default, at least using Hibernate, the default fetch type is Lazy, so you need to do a join fetch instead of a join. Also, you should have select distinct. Try:
SELECT distinct a FROM AName a LEFT JOIN fetch a.aNameRoleList r WHERE r.inactiveflag='N' and r.aNameRolePK.nametype='5'
References: Default fetch type for one-to-one, many-to-one and one-to-many in Hibernate
After more testing, I realized the join was working, but not the "r.aNameRolePK.nametype='5'". But if I changed that to "r.aNameRolePK.nameidx=1" it works. So, it was just the nametype field, which we have defined as a char(2) in the database. The problem is with the spaces in a char field and it is discussed here: Java NamedQuery String Problem. It looks like the recommended way to resolve this is to implement an EclipseLink SessionCustomizer. For testing I changed the named query to
SELECT a
FROM AName a LEFT JOIN a.aNameRoleList r
WHERE r.inactiveflag='N' and trim(trailing from r.aNameRolePK.nametype)=5
This returns the expected records.

#OneToMany relationship property not filled

I have implemented Joined, Multiple Table Inheritance.
There is a 'parent' table pois and two sub tables: xPois and yPois and in turn I have an abstract PoiDao class as well as a XPoiDao and a YPoiDao class extending PoiDao.
A poi may have multiple reservations but a reservation belongs to exactly one poi.
Named queries defined in the child table DAOs work well for attributes defined in the respective (direct) table hierarchy. The parent table has a foreign key relationship to another table named reservations (table reservations holds the foreign key of table pois). The problem is that the records from this reservations table get not fetched.
Running this SQL statement in MySql Workbench gets the desired resultset:
SELECT * FROM xPois pp
LEFT JOIN pois p ON pp.poiId = p.poiId
LEFT JOIN reservations r ON p.poiId = r.poiId
WHERE pp.xPoiId = '2011';
In Eclipse I can see {IndirectList: not instantiated} when I inspect the xDao instance in debug mode.
How can I get the records from this table being stored in the PoiDao using JPA?
public abstract class PoiDao implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="poiId")
private Integer poiId;
#OneToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name="addressId",insertable=true,
updatable=true, unique=true, nullable=false)
private AddressDao address;
#Embedded
private GeoLocationDao geoLocation;
#Convert("poiTypeConverter")
private ServiceTypeEnum poiType;
#Column(name="operator")
private String operator;
#Column(name="reservable")
private boolean reservable;
#OneToMany(orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinColumn(name="poiId", insertable=true, updatable=true)
private List<ReservationDao> existingReservations;
...
}
#Entity
#Table(name="xPois")
#DiscriminatorValue("X")
#NamedQueries({
#NamedQuery(name="XPoiDao.findAll", query="SELECT p FROM XPoiDao p"),
#NamedQuery(name="XPoiDao.findByXPoiId",
query="SELECT pp FROM XPoiDao pp LEFT JOIN PoiDao p ON pp.poiId = p.poiId "
+ "LEFT JOIN ReservationDao r ON p.poiId = r.poiId WHERE pp.xPoiId = :xPoiId")
})
#ObjectTypeConverters({
#ObjectTypeConverter (
name="xPoiStatusConverter",
dataType=java.lang.String.class, // type in DB
objectType=XPoiStatusEnum.class, // Java type
conversionValues={
#ConversionValue(dataValue="FREE", objectValue="FREE"),
#ConversionValue(dataValue="OCCUPIED BY VALUE", objectValue="OCCUPIED_BY_VALUE"),
#ConversionValue(dataValue="OCCUPIED MANUALLY", objectValue="OCCUPIED_MANUALLY"),
#ConversionValue(dataValue="BLOCKED", objectValue="BLOCKED")
}
)
})
public class XPoiDao extends PoiDao implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2496267921294255723L;
// #Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column(name="xPoiId")
private String xPoiId;
#Convert("xPoiStatusConverter")
#Column(name="status")
private XPoiStatusEnum status;
#Embedded
private ContactDao contact;
// #OneToMany(orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
// #JoinColumn(name="poiId",insertable=true,updatable=true)
// private List<ReservationDao> existingReservations;
#OneToMany(orphanRemoval=true, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinColumn(name="parkingPoiId",insertable=true,updatable=true)
private List<OperatingHourDao> operatingHours;
...
}
You've got FetchType.LAZY in there. Do you get an empty list when you try to access it? Debuggers might not trigger the fetch requests.

JPA -- Using the one-to-one dependency relationship on insertion

I have 2 entity classes with one-to-one dependencies on their primary keys:
The primary table:
#Entity
#Table(name = "tablePrimary")
#XmlRootElement
//...
public class TablePrimary implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "code")
private String code;
// set the dependency of table2 to this class
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Table2 table2inst;
// ...
} // end of class TablePrimary
The dependent table:
#Entity
#Table(name = "table2")
#XmlRootElement
//...
public class Table2 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Basic(optional = false)
#Column(name = "name")
private String name;
#MapsId
#OneToOne(mappedBy = "table2inst")
#JoinColumn(name = "id")
private TablePrimary tablePrimaryInst;
//...
} // end of class Table2
Whenever there is a row with say, id==55 in TablePrimary, there is
a row with the same id==55 in Table2 and vice-versa.
So in essence, these two tables are one table in logical level-- split into 2 physical tables for practicality.
When i'm inserting a row into the "logical" table,
i first am inserting to TablePrimary-- the primary table in the relationship,
getting the value of id==55 field of this new row i just inserted and inserting a row to
Table2 with that id value.
As part of this, i'm checking, just in case,
whether a row with id==55 is already in Table2.
Is there a better way of doing this?
Does JPA have a feature to make these two insertions to these two physical tables
by using the 1-1 dependency I configured on them-- without me having to do it "manually" in the code? Or a control feature on the id fields of the tables I set the dependency on?
If there is-- how is done? how does it handle the key value collision in the dependent table-- Table2?
A similar thing will come up on deletion. However, i'm not there yet, and might figure out out of this.
TIA.
You can take advantage of JPA cascading. You will have to define a cascade on the owning side (the one with the join column). If you have set the owning side of the relationship and persist the owning side, the inverse side will be persisted as well:
TablePrimary tp = new TablePrimary();
Table2 t2 = new Table2();
t2.setTablePrimaryInst(tp);
entityManager.persist(t2);
The 'mappedBy' element is supposed to be placed on the inverse side. You entities could look like this:
public class Table2 ...
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "tp_id")
private TablePrimary tablePrimary;
public class TablePrimary...
#OneToOne(mappedBy="tablePrimary")
private Table2 table2;

How to make a CriteriaBuilder join with a custom "on" condition?

I want make a query where I join 2 tables, using the CriteriaBuilder. In MySQL the query I'm trying to make would look like this:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
AND item.type_id = 1
I want to get all orders and if they have an item of type #1, I want to join with this item. However, if no item of type #1 is found, I still want to get the order. I can't figure out how to make this with the CriteriaBuilder. All I know how to make is:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
Join<Item, Type> type = order.join(Item_.type, JoinType.LEFT);
cq.select(order);
cq.where(cb.equal(type.get(Type_.id), 1));
This query is broke, since it results in something like this in MySQL:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
WHERE item.type_id = 1
The result will only contain orders with items of type #1. Orders without are excluded. How can I use the CriteriaBuilder to create a query like in the first example?
It is possible starting from the version 2.1 of JPA using the on method Join<Z, X> on(Predicate... restrictions);
Here is how:
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
item.on(cb.equal(item.get(Item_.type), 1));
I think this is the same problem as posed in this question. It looks like it is not possible in CriteriaBuilder. It is possible in Hibernate Criteria API, but that probably won't help you.
JPA Criteria API: Multiple condition on LEFT JOIN
I know this question was made a long time a go, but recently a had the same problem and i found this solution from an Oracle forum, i copied and pasted just in case the link is not longer available.
MiguelChillitupaArmijos 29-abr-2011 1:41 (en respuesta a 840578) Think
you should use something like:
em.createQuery("SELECT DISTINCT e.Id" +
" from Email e " +
" left join e.idEmailIn e2 *with* e2.responseType = 'response'" +
" where e.type = 'in' and e.responseMandatory = true").getSingleResult();
An this is the link.
JPA Criteria : LEFT JOIN with an AND condition
There is a workaround if you are using Hibernate 3.6 with JPA 2.0
It is not the better solution, however it works perfect for me.
I´ve duplicate the entity with the #Where hibernate annotation.It means that everytime you use the join with this entity, hibernate will add the extra condition on the join statement at generated SQL.
For instance, initially we have the follow example:
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long id;
#Id
#Column(name = "PERSON_NAME")
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
}
#Entity
#Table(name = "ADDRESS")
public class Address {
#Id
#Column(name = "ADDRESS_ID")
private Long id;
#Id
#Column(name = "ADDRESS_STREET")
private String street;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
In order to add extra conditions on criteria Join, we need duplicate the Address #Entity mapping , adding the #Where annotation #Where(clause = " ADDRESS_TYPE_ID = 2").
#Entity
#Table(name = "ADDRESS")
#Where(clause = " ADDRESS_TYPE_ID = 2")
public class ShippingAddress {
#Id
#Column(name = "ADDRESS_ID")
private Long id;
#Id
#Column(name = "ADDRESS_STREET")
private String street;
#OneToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
Also, we need to add the duplicate mapping association for the new entity.
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long id;
#Id
#Column(name = "PERSON_NAME")
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
#OneToOne(mappedBy = "person")
private ShippingAddress shippingAddress;
}
Finally, you can use a join with this specific Entity in your criteria :
PersonRoot.join(Person_.shippingAddress, JoinType.LEFT);
The Hibernate Snippet SQL should seems like this :
left outer join
address shippingadd13_
on person11_.person_id=shippingadd13_.person_id
and (
shippingadd13_.ADDRESS_TYPE_ID = 2
)
ON clause is supported in Hibernate 4.3 version, anyone is aware if there is a parameter indexing issue between the parameter index of the additional custom conditions with the index of the existing mapping filters when doing an outer join with ON clause?
Using the Person entity class below as an example, say I am adding this filter to limit the address types and the filter is enabled to populate the IN clause. The parameter index for the IN clause will cause the issue [2] when I add additional conditions (such as using 'street' column) part of the ON clause. Is is a known issue?
[1] #Filter(name = "addressTypes", condition = "ADDRESS_TYPE in (:supportedTypes)")
[2]
Caused by: ERROR 22018: Invalid character string format for type BIGINT.
private Set addresses;