How to get non related entity using JPA - java

I have two entities
#Entity
public class Language {
#Id
#GeneratedValue
private int id;
private String name;
private String isoCode;
}
and the another entity
#Entity
public class MainEntity {
#Id
#GeneratedValue
private int id;
private String anotherField;
private Language language; //(here I want to return the full entity)
}
The entities don't have a database relation just a string field, Now I want to do a query for MainEntity but I want to get the full entity
For example
Language
value
id
1
name
English
isoCode
EN
MainEntity
value
id
a
name
xyz
language
EN
I have that repository
public interface MainRepository extends JpaRepository<MainEntity, Integer>{
User findAll();
}
but I want that when I do a search on my primary entity it brings me the entire language entity
for example
MyEntity.Language.getName()

Use native query to read JPA objects? See this simplified example where you can use any valid native sql query syntax.
You could put Language getLanguage() function to MainEntity class.
String sql = "Select * From MyEntityTable Where id between (?1 and ?2)";
Query query = entityMgr.createNativeQuery(sql, MyEntity.class);
query.setParameter(1, 1001);
query.setParameter(2, 2002);
List<MyEntity> list = query.getResultList();

If you don't want to make a reletion between MainEntity and Language you have to make a native query. (As is you can't make also a jpql query because you should have at least the String language in the MainEntity in order to make a join on String isoCode in Language but as I understand I think you don't want to do that).
So you have to perform a native query returning an interface as stated here
public interface IntermediateObject {
int getId();
String getName();
int getLanguageId();
String getLanguageName();
String getIsoCode();
}
In the repository:
#Query("select m.id as id, m.name as name, l.id as languageId, l.name as languageName, l.isoCode as isoCode MainEntity m join Language l on m.language = l.isoCode", native=true)
List<IntermediateObject> someMethod();
In the MainEntity or better another dto you have to declare a constructor:
public class AnotherDto {
private int id;
private String name;
private LanguageDto language;
public AnotherDto(int id, String name, int languageId, String languageName, String isoCOde) {
this.id = id;
this.name = name;
this.language = new LanguageDto(languageId, languageName, isoCode);
}
}
public class LanguageDto {
private int id;
private String name;
private String isoCode;
public LanguageDto(int id, String name, String isoCode) {
this.id;
this.name = name;
this.isoCode = isoCode;
}
}
And in the service layer you have to construct AnotherDto from IntermediateObject
public List<AnotherDto> someMethod() {
return repository.someMethod().stream().map(i -> new AnotherDto(i.getId(), i.getName(), i.getLanguageId(), i.getLanguageName(), i.getIsoCode())).collect(Collectors.toList());
}
and now you have the language object populated.
Anyway I suggest to map the entities instead to do all of this.

Related

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"
)
})

Projection in JPQL with property of type list

How I can made a select of a property of type list on JPQL?
example:
#Entity
public class Person {
#id
private Long id;
private String name;
private String lastname;
private String birthdate;
#OneToMany
private List<Phone> getPhones();
...
}
#Entity
public class Phone {
#id
private Long id;
private String number;
...
}
And on repository I want a projection, so:
public interface IPersonProjection {
Long getId();
String getName();
List<Phone> phones();
}
#Repository
public interface IAtendimentoRepository extends JpaRepository<Atendimento, Long> {
#Query("SELECT P.id, P.name, P.phones FROM Person P ")
List<IPersonProjection> findAllProjected();
}
But when I try this (SELECT P.id, P.name, P.phones FROM Person P) occur an error on syntax of SQL.
As specified in Spring Data Docs, you should have accessors of your properties in your Projection Interface. So i think you should change the name of phones method of IPersonProjection to getPhones.

Query multi tables' columns with join in spring data jpa

Description:
I have two tables, Shop_Employee and Shop_Employee_Type. I wanna display typeName directly when show the employee detail information, but I don't want to config the relationship(OneToMany or ManyToOne) between this two entity. Because this will load all Shop_Employee_Type column's value, but these values are useless for me, I just need typeName of Shop_Employee_Type.
Below is my code, but it doesn't work.
ShopEmployeeType:
#Entity
#Data
//#DynamicUpdate
public class ShopEmployeeType {
#Id
private String typeId;
private String shopId;
private String typeName;
private Integer typeStatus;
private String typeDescription;
}
Shop_Employee:
#Entity
#Data
public class ShopEmployee {
#Id
private String employeeId;
private String shopId;
private String typeId;
private String name;
private String code;
private String phone;
private Integer status;
private String idcardNumber;
private String image;
//#Transient
private String typeName;
public ShopEmployee() {
}
}
Repository:
#Query(value = "select u.*,t.type_name from shop_employee u inner join shop_employee_type t on u.type_id=t.type_id", nativeQuery = true)
List<ShopEmployee> findAllData();
This could show typeName as I wished, but there is an error appears when I save a new entity Shop_Employee; If I add a #Transient for 'typeName', It could save successfully, but the value of 'typeName' is null when I query entity Shop_Employee.
Your query should return two Objects Shop_Employee and a String, so the return results should not be List<ShopEmployee> it should be :
#Query(value = "select u.*, t.type_name from shop_employee ...", nativeQuery = true)
List<Object[]> findAllData();
Then you can get the ShopEmployee using :
List<Object[]> list = findAllData();
for(Object[] obj : list){
ShopEmployee shopEmployee = (ShopEmployee) obj[0];
String type_name = (String) obj[1];
}
So, in ShopEmployee entity you don't need to use :
//#Transient
//private String typeName;

Hibernate mapping of internationalized database

I want to get international content from database based on locale provided in hibernate query. This is a question about hibernate mapping but please feel free to propose better database design if mine is wrong.
My DB design (simplified):
db design
So I have table with non translatable data and additional table with translated content but with additional field "locale" for distinction of language.
My java classes looks like this:
public class Car {
private Long id;
private Long length;
private Long weight;
private CarTranslated carTranslated;
// getters and setters
public class CarTranslated {
private Long id;
private Long carId;
private String desc;
// getters and setters
I want to be able to get one car with single query. With regular jdbc I would use something like this sql query:
public Car getById(Long id, Locale locale) {
Car c = new Car();
String sql = "select c.car_id, c.length, c.weight, ct.id, ct.descryption,
ct.car_id as "Translated car_id" from car c join car_translated ct on
(c.car_id = ct.car_id) where c.car_id ="+ id+" and ct.locale ='"+locale+"'";
// code to set fields of the object using ResultSet
return c;
}
What would be a hibernate annotation mapping and query for this setup? I tried several attempts but to no avail. Currently my best attempt was as below:
Mapping:
#Entity
#Table(name="CAR")
public class Car {
#Id
#Column(name="car_id")
private Long carId;
#Column (name="weight")
private Long carWeight;
#Column (name="length")
private Long carLength;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name ="CAR_ID")
private CarTranslated localized;
// getters and setters
#Entity
#Table(name="CAR_TRANSLATED")
public class CarTranslated {
#Id
#Column (name="id")
private Long id;
#Column (name="car_id")
private Long carId;
#Column (name="descryption")
private String desc;
#Column(name="locale")
private Locale locale;
DAO:
public Car getCarById(Locale locale, Long id) {
Car car = new Car();
try {
Session session = HibernateUtils.getSessionFactory().openSession();
Criteria cr = session.createCriteria(Car.class)
.add(Restrictions.eq("carId", id));
Criteria cr1 = session.createCriteria(CarTranslated.class)
.add(Restrictions.eq("locale", locale));
car = (Car) cr.uniqueResult();
car.setLocalized((CarTranslated) cr1.uniqueResult());
} catch (Exception e) {
System.out.println(e.getMessage());
}
return car;
}
This is a work-around and I'm wondering what would be a proper way to do this?
You should have an annotation on both columns when mapping to a FK. (JavaDoc)

java.lang.AbstractMethodError during createQuery

I am attempting to retrieve a list of results from my database, by following an example in this answer. Howeverm, I keep getting the following error:
java.lang.AbstractMethodError: org.hibernate.ejb.EntityManagerImpl.createQuery(Ljava/lang/String;Ljava/lang/Class;)Ljavax/persistence/TypedQuery;
Here is my code, to denote how I am calling this:
#Entity
#Table(name = "MY_TABLE")
public class CoolsEntity implements Serializable {
#Id
#Column(name = "ID", columnDefinition = "Decimal(10,0)")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
#Column(name = "COOL_GUY_NAME")
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name= name;
}
}
This code below generates the error:
final String sql = "select c from CoolsEntity c";
final TypedQuery<CoolsEntity> query = em.createQuery(sql, CoolsEntity.class);
final List<CoolsEntity> results = query.getResultList();
return results;
However, if I do something like this, I can see the results:
final String sql = "select c from CoolsEntity c";
final Query query = em.createQuery(sql);
#SuppressWarnings("unchecked")
final List<CoolsEntity> results = query.getResultList();
return results;
All of the references to em are imported through this package:
import javax.persistence.EntityManager
Shouldn't the two queries above generate the same result? Am I missing a cast to the List interface to allow this to work in the typed query?
You have an AbstractMethodError exception which is thrown when an application tries to call an abstract method.
You have quite a mix of Hibernate and JPA versions.
TypedQuery was introduced in JPA 2.0 and Hibernate implements this specification since 3.5.X
Suggesstion : Use implementation from Hibernate version 3.6.3 (or higher).
you are probably using two differnet version of interface EntityManager and implementation EntityManagerImpl.

Categories

Resources