Update nested entity in Spring Data / JPQL - java

I have these 2 JPA entities:
#Entity
public class Link {
// some other fields...
#ManyToOne
#JoinColumn(name = "link_status_id")
private LinkStatus status;
}
#Entity
public class LinkStatus {
private String code;
private String name;
private int order;
}
I'm trying to write a query that updates the status on those links that match some criteria, these are my 2 attempts:
#Query("update Link l set l.status = :targetLinkingStatus where l.release.id in :releaseIds and l.status.code = :currentLinkingStatusCode")
#Modifying
void updateLinksStatus(List<Long> releaseIds, LinkStatus targetLinkingStatus, String currentLinkingStatusCode);
#Query("update Link l set l.status.id = :targetLinkingStatusId where l.release.id in :releaseIds and l.status.code = :currentLinkingStatusCode")
#Modifying
void updateLinksStatus(List<Long> releaseIds, Long targetLinkingStatusId, String currentLinkingStatusCode);
In both cases I'm getting this error:
o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: syntax error at or near "cross"
How should I write this query?

Related

Use enumerations in JPA Query

I'm trying to use JPA to update an entity property which is an enumeration in my domain model, and I can't make it work.
I have the following entities:
public class A {
#Id
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "status")
private Status status;
}
public class Status {
#Id
#Enumerated(EnumType.STRING)
#Column(name = "id")
private StatusId id;
public enum StatusId {
B, C, D, E, F
}
}
and this JPA repository:
public interface ARepository extends JpaRepository<A, Long> {
#Modifying
#Transactional
#Query("UPDATE A SET status = ?2 WHERE id = ?1")
void updateStatus(Long id, Status.StatusId status);
}
Executing this method throws the following error:
Caused by: java.lang.IllegalArgumentException: Parameter value [B] did not match expected type [com.***.Status (n/a)]
Any ideas on how to solve this?
The error says:
Parameter value [B] did not match expected type [com.***.Status
and in your query you are effectively trying to assign an object of type Status.StatusId to a field of type Status
You should try to change the query to something like this:
UPDATE A SET status.id = ?2 WHERE id = ?1

Hibernate - Mapping three tables with a single save

I am trying to create a project that will use Hibernate to store the objects to the database.
If I simply insert (save) an object that does not contain a mapping with another table everything works fine. However, I have a case where there is a connection between three tables. The tables are the Asset, MonetaryValue and CurrencyType (see below).
When an Asset is inserted, the monetaryValueType must be provided (by the user ) along with the currency type. Asset holds a OneToOne relation with the MonetaryValueType and MonetaryValueType holds a OneToOne relation to the CurrencyType Table.
More specifically, below you will find the database tables.
Asset(asset_id,ownerIID,valueID,samID), where valueID is the foreign key to the MonetaryValueType Table (OneToOne undirectional mapping)
MonetaryValueType(mvID, mValue,currencyId), where currencyID is the foreign key to the CurrencyType Table (OneToOne undirectional mapping)
CurrencyType(currencyID,currField,currValue,currSymbol).
The problem is that every time I create the asset object and I am calling the asset service to save the element, Hibernate either create a select query that tries to select from a database table I did never define or Inserts in the currency field with wrong column names (i.e. currency_field instead of currField etc.)
I've tried to play with all the Cascade types but nothing seems to work.
Asset.java
#Entity
#Table(name="asset")
public class Asset implements java.io.Serializable{
#Id
#Column(name="assetID", unique = true, nullable = false)
private long assetID;
#Column(name="ownerID")
private long ownerID;
#OneToOne
#JoinColumn(name="valueID")
private MonetaryValueType monetaryValueType;
#Column(name="samID")
private long samID;
------------Constructor, Getters , Setters-----
MonetaryValueType.java
#Entity
#Table(name="monetaryvaluetype")
public class MonetaryValueType{
#Id
#Column(name="mvID",nullable = false,unique = true)
private Long id;
#Column(name="mValue")
private double mValue;
#OneToOne
#JoinColumn(name="currencyId")
private CurrencyType currency;
------------Constructor, Getters , Setters-----
CurrencyType.java
#Entity
#Table(name="currencytype")
public class CurrencyType implements java.io.Serializable {
#Id
#Column(name="currencyID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int currencyID;
#Column(name="currField")
private String currField;
#Column(name="currValue")
private String currValue;
#Column(name="currSymbol")
private String currSymbol;
------------Constructor, Getters , Setters-----
Every entity holds its own DAO,DAOImpl, Service and ServiceImpl class. For instance, for the asset class the DAOImpl and ServiceImpl can be found below:
AssetDAOImpl.java
#Repository
public class AssetDAOImpl implements AssetDAO{
private Logger logger = LoggerFactory.getLogger(this.getClass());
//entity manager field
#Autowired
private EntityManager entityManager;
#Override
public List<Asset> findAll() {
Session currentSession = entityManager.unwrap(Session.class);
//create a query
Query theQuery =
currentSession.createQuery("from asset",Asset.class);
//execute query and get result list
List<Asset> aModelElements = theQuery.getResultList();
//return the results
return aModelElements;
}
#Override
public Asset findById(int theId) {
return null;
}
#Override
public Asset insert(Asset assetElement) {
//Session currentSession = entityManager.unwrap(Session.class);
boolean success = false;
try {
entityManager.persist(assetElement);
logger.info("Asset -> {}", assetElement);
return assetElement;
}
catch(Exception e){
e.printStackTrace();
}
return null;
}
AssetServiceImpl.java
#Service
public class AssetServiceImpl implements AssetService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private AssetDAO assetDAO;
#Autowired
public AssetServiceImpl(AssetDAO theAssetDAO){
assetDAO=theAssetDAO;
}
#Override
#Transactional
public List<Asset> findAll() {
return assetDAO.findAll();
}
#Override
#Transactional
public Asset findById(int theId) {
return assetDAO.findById(theId);
}
#Override
#Transactional
public Asset insert(Asset theAsset) {
assetDAO.insert(theAsset);
return theAsset;
}
...
The class that I use to fill the asset class (and all its children) is:
UniqueIDGenerator uniqueIDGenerator = new UniqueIDGenerator();
CurrencyType currencyType = new CurrencyType();
Asset asset = new Asset();
MonetaryValueType monetaryValueType = new MonetaryValueType();
currencyType.setCurrValue(ctx.value().monetaryValueType().currency().CurrencyType().getText());
currencyType.setCurrSymbol("currency");
monetaryValueType.setId(uniqueIDGenerator.nextId());
monetaryValueType.setmValue(Double.parseDouble(ctx.value().monetaryValueType().mValue().getText()));
monetaryValueType.setCurrency(currencyType);
asset.setMonetaryValueType(monetaryValueType);
asset.setAssetID(uniqueIDGenerator.nextId());
asset.setOwner(uniqueIDGenerator.nextId());
asset.setSamID(uniqueIDGenerator.nextId());
assetService.insert(asset);
Whenever I call the class mentioned above, I get the following error:
Hibernate:
insert
into
element1
(datefrom, dateto, description, name, statusid, samid)
values
(?, ?, ?, ?, ?, ?)
2019-08-05 20:19:00 INFO MyClass:63 - the result is:true
Hibernate:
select
monetaryva_.mvid,
monetaryva_.currency_id as currency3_57_,
monetaryva_.m_value as m_value2_57_
from
monetaryvaluetype monetaryva_
where
monetaryva_.mvid=?
2019-08-05 20:19:01.084 WARN 56712 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1054, SQLState: 42S22
2019-08-05 20:19:01.084 ERROR 56712 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'monetaryva_.currency_id' in 'field list'
As you can see, hibernate created columns (currency_id instead of currencyID) that are not in accordance with my database tables even though I used the #Column annotation.
Use following two lines in your application.properties file
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

returning a list of instances by the foreign key

I have two models, Owner and Contract. A contract has an instance of an owner, owner does not have a list of contracts. I'm trying to query my list of contracts, to return a list filtered by owner, ie, a list of contracts by owner.
I had tried to follow previous examples and use Criteria to write a custom query, but, following suggestions I've checked the docks and tried to use named queries instead, however, I'm still really struggling.
There was an unexpected error (type=Internal Server Error, status=500).
Named parameter not bound : ownerId; nested exception is org.hibernate.QueryException: Named parameter not bound : ownerId
My models look like this:
#Entity
#Table(name="Contracts")
#NamedQueries({
#NamedQuery(
name = "Contract.allContractsByOwner",
query = "SELECT c FROM Contract c WHERE c.owner.id LIKE :ownerId"
)
})
public class Contract {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#ManyToOne
private Owner owner;
#Column
private double price;
#Column
private String deliverDate;
public Contract(Owner owner, double price, String deliverDate) {
this.id = id;
this.owner = owner;
this.price = price;
this.deliverDate = deliverDate;
}
and
#Entity
#Table(name="Owners")
public class Owner {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
private String name;
public Owner(String name){
this.name = name;
}
my contractRepoImpl
#Service
public class ContractRepositoryImpl implements ContractRepositoryCustom {
ContractRepository contractRepository;
#Autowired
EntityManager entityManager;
public List allContractsByOwner(Long ownerId) {
List contracts = entityManager.createQuery(
"SELECT c FROM Contract c WHERE c.owner.id LIKE :ownerId", Contract.class)
.getResultList();
return contracts;
}
}
which I name in my ContractRepo and ContractRepoCustom files, and then in my controller I map to it like so. But, when I query it in my browser I get the error in my terminal.
#GetMapping(value="/owners/{ownerId}/contracts")
public List allContractsByOwner(#PathVariable("ownerId") Long ownerId){
return contractRepository.allContractsByOwner(ownerId);
}
I appreciate this is probably beginners mistakes, I am trying to follow docs but get a bit stuck with syntax & where annotations need to go.
Thanks JB Nizet, got there in the end
I added parameters to my contractRepoImpl
#Service
public class ContractRepositoryImpl implements ContractRepositoryCustom {
ContractRepository contractRepository;
#Autowired
EntityManager entityManager;
public List allContractsByOwner(Long id) {
List contracts = entityManager.createQuery(
"SELECT c FROM Contract c WHERE c.owner.id = :ownerId", Contract.class)
.setParameter("ownerId", id)
.getResultList();
return contracts;
}
}
that then produced a SQL error, which I fixed by changing my #NamedQuery from 'LIKE' to '=' in my Contract class...
#NamedQueries({
#NamedQuery(
name = "Contract.allContractsByOwner",
query = "SELECT c FROM Contract c WHERE c.owner.id = :ownerId"
)
})

JPA CriteriaQuery join three tables not directly navigable

i need to translate this sql query to jpa criteria:
SELECT tbl1.id_t1, tbl2.name, tbl3.name, tbl4.symbol, tbl1.limit, tbl1.value, tbl1.uncertainty
FROM table_1 AS tbl1
JOIN table_2 AS tbl2 ON tbl2.id_t2=tbl1.id_t2
JOIN table_3 AS tbl3 ON tbl3.id_t3=tbl1.id_t3
JOIN table_4 AS tbl4 ON tbl4.id_t4=tbl1.id_t4
WHERE (tbl2.id_l=1 AND tbl3.id_l=1) AND tbl1.id_s=1;
my mapping between pojo and database table are as follows:
Table_1
#Entity
#Table("table_1")
public class Table1 {
#Id
#Column(name="id_t1")
private Long idRowT1
#ManyToOne
#JoinColumn(name="id_t2")
private Table2 tbl2;
#ManyToOne
#JoinColumn(name="id_t3")
private Table3 tbl3;
#ManyToOne
#JoinColumn(name="id_t4")
private Table4 tbl4;
#Column(name="limit")
private String limit;
#Column(name="value")
private String value;
#Column(name="uncertainty")
private String uncertainty;
// getter and setter
}
Table_2
#Entity
#Table("table_2")
public class Table2 {
#Id
#Column(name="id_t2")
private Long idT2;
// getter and setter
}
Table_2_lang
#Entity
#Table("table_2_lang")
#IdClass(Table2LangPK.class)
public class Table2Lang {
#Id
#Column(name="id_t2")
private Long idT2;
#Id
#Column(name="id_l")
private Lang l;
#Column(name="name")
private String name;
// getter and setter
}
Table_3
#Entity
#Table("table_3")
public class Table3 {
#Id
#Column(name="id_t3")
private Long idT3;
// getter and setter
}
Table_3_lang
#Entity
#Table("table_3_lang")
#IdClass(Table3LangPK.class)
public class Table3Lang {
#Id
#Column(name="id_t3")
private Long idT3;
#Id
#Column(name="id_l")
private Lang l;
#Column(name="name")
private String name;
// getter and setter
}
Table_4
#Entity
#Table("table_4")
public class Table4 {
#Id
#Column(name="id_t4")
private Long idT4;
#Column(name="name")
private String name;
// getter and setter
}
To send data from business layer to front-end i'm using value objects defined as follows:
Simple entity
public class SimpleEntityVO {
private Long entityId;
private String name;
// getter and setter
}
Complex Entity
public class SimpleEntityVO {
private Long entityId;
private SimpleEntityVO tbl2VO;
private SimpleEntityVO tbl3VO;
private SimpleEntityVO tbl4VO;
// ... other field of table_1
// getter and setter
}
In my EJB i need to implement a method that return a list of ComplexEntityVO starting from Table_1
...
private CriteriaBuilder cB = eM.getCriteriaBuilder();
public List<ComplexEntityVO> findAll(Long id_s, Long id_l) {
CriteriaQuery<ComplexEntityVO> cQ = cB.createQuery(ComplexEntityVO.class)
Root<Table1> tbl1Root = cQ.from(Table1.class);
// UPDATE BEGIN
Root<Table2Lang> tbl2Root = cQ.from(Table2Lang.class);
...
Selection<SimpleEntityVO> sESTbl2 = cB.construct(SimpleEntityVO.class, tbl2Root.get(Table2Lang_.id_t2), tbl2Root.get(Table2Lang_.name));
// The selection for table_3_lang and table_4 are the same
// UPDATE END
TypedQuery<ComplexEntityVO> tQ = eM.createQuery(cQ);
}
...
To achieve the results i've tried with join betwen Table1 and Table2Lang, tried with selection like the one exposed below
`Selection<SimpleEntityVO> sES = cB.construct(SimpleEntityVO.class, ...);`
using Root for lang table, tried with solution exposed here
https://community.oracle.com/message/10795956#10795956
but when i try to execute this statement
`cQ.select(cB.construct(ComplexEntityVO.class, id_t1, SimpleEntityVO)`
or this
`cQ.multiselect(...)`
i get the: IllegalArgumentException
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: , near line 1, column 64
[select new com.example.vo.ComplexEntityVO(generatedAlias0.id_t1,
new com.example.labims.vo.SimpleEntityVO(generatedAlias1.table2.id_t2, generatedAlias1.name),
new com.example.vo.SimpleEntityVO(generatedAlias2.table_3.id_t3, generatedAlias2.name),
new com.example.vo.SimpleEntityVO(generatedAlias3.id_t4, generatedAlias3.name),
generatedAlias0.limit, generatedAlias0.value, generatedAlias0.uncertainty)
from com.example.Table1 as generatedAlias0,
com.example.model.Table2Lang as generatedAlias1,
com.example.model.Table3Lang as generatedAlias2,
com.example.model.Table4 as generatedAlias3
where ( generatedAlias0.id_s=:param0 ) and ( ( generatedAlias1.lang.id_l=:param1 ) and ( generatedAlias2.lang.id_l=:param1 ) )]
From the cause of execption understanded that i can't instanciate new object inside select or multiselect statement, but i don't find a way to achieve the original SQL query using criteria API.
UPDATE
i've added an excerpt of what i've tried to achieve the result between //UPDATE BEGIN and //UPDATE END
I think make hibernate show sql == true
and take query by console,test showed query your databases and find error hbernate not generate query correct
There are two approaches to solve this problem.
Add a constructor method to ComplexEntityVO like this:
public ComplexEntityVO(Long id, Long simpleId2, String simpleName2 /* etc ... */) {
this.simpleEntityVo = new SimpleEntityVO(simpleId2, simpleName2);
// etc for other associations
}
add a ProjectionList to your query, return a List<Object[]> instead of a List<ComplexEntityVO> and then iterate over the results like so
for(Object[] o: results) {
ComplexEntityVO cvo = new ComplexEntityVO((Long)o[0]);
new SimpleEntityVO vo2 = new SimpleEntityVO((Long) o[1], (String) o[2]);
cvo.setTbl2VO(vo2);
// ... etc for other associations
}
Although the second is uglier I would prefer it, since it is more flexible, and allows more opportunities for debugging, logging etc.
See AliasToBeanResultTransformer(MyDTO.class) fails to instantiate MyDTO

Hibernate Criteria on Referenced table

I have a function that merges two tables, each of these tables has a column that I want to filter.
#Entity
public class Contacts {
#Id
private int id;
#ManyToOne //reference user_id = id
private User user;
#ManyToOne //reference people_id = id
private People people;
//getters and setters
}
#Entity
public class User {
private int id;
private int name;
private Enterprise enterprise;
//getters and setters
}
#Entity
public class People {
private int id;
private int name;
//..others fields
private Enterprise enterprise;
//getters and setters
}
I need to list all "Contacts" where my enterprise id = 1. On a simple select (SQLServer), it will be:
SELECT c.* FROM CONTACTS c
INNER JOIN User u ON u.id = c.user_id
INNER JOIN People p on p.id = p.people_id
WHERE u.empresa_id = 1
I can't figure out how to do it with Criteria API, I already tried the follow code, but I keep receiving an error.
//code..public List<Obj> list(int id) {
Criteria crit = session.createCriteria(Contacts.class);
crit.add(Restrictions.eq(user.enterprise.id, id)); //it doesn't work!
crit.list();
}
org.hibernate.QueryException: could not resolve property: user.enterprise.id of: sys.com.model.Contacts
here i am writing code for sample using criteria.
public List<Student_document> getUserById(int id) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
Student_document.class);
criteria.add(Restrictions.eq("user_document.user_Id", id));
return criteria.list();
}

Categories

Resources