There are two tables PersonEntity and cityentity. PersonEntity in the database is linked to cityentity by the external key fk_cityid. I need to select all the records (names) of the PersonEntity table with the given CityId. Join is used everywhere for this, but in this case I don't need data from the cityentity table, only the name field of the PersonEntity table. Here is a description of the classes:
#Entity
public class PersonEntity {
private Long id;
private String name;
private CityEntity cityId;
}
#Entity
public class CityEntity {
private Long id;
private String name;
}
Here is the HQL query:
#Repository
public interface PersonEntityRepository extends JpaRepository<PersonEntity, Long> {
#Query("select p.name FROM PersonEntity p where (p.name = :name or :name is null) " +
"and (p.cityId = :cityId or :cityId is null)")
List<PersonEntity> findByNameAndCity (
#Param("name") String name,
#Param("cityId") CityEntity cityId);
}
tried by id:
#Query("select p.name FROM PersonEntity p where (p.name = :name or :name is null) " +
"and (p.cityId.id = :cityId or :cityId is null)")
List<PersonEntity> findByNameAndCity (
#Param("name") String name,
#Param("cityId") Long cityId);
In both cases, the error is: "the data type could not be determined".
options for calling the function:
servisPerson.findByNameAndCity (null, cityId);
or
servisPerson.findByNameAndCity (name, null);
In fact, there are more than two parameters. I only show two for simplification.
servisPerson.findByNameAndCity (name, age, ..., cityId);
Person entity should look something like this
#Entity
public class PersonEntity {
private Long id;
private String name;
#OneToOne
#JoinColumn(name = "city_id")
private CityEntity city;
}
Than you can write your query like this
List<PersonEntity> findByNameAndCityOrNameIsNullOrCityIsNull(String name, CityEntity city);
Spring Data JPA is so smart to generate the query for you under the hood.
BTW, you attempted to write JPQL, not HQL.
EDIT (reaction on comment about long method names):
I would suggest to avoid creating JPQL query, because is is more error prone. If you don't like lengthy method name, you can wrap it into shorter default method within repository:
#Repository
public interface PersonEntityRepository extends JpaRepository<PersonEntity, Long> {
List<PersonEntity> findByNameAndCityOrNameIsNullOrCityIsNull(String name, CityEntity city);
default List<PersonEntity> shortName(String name, CityEntity city) {
return findByNameAndCityOrNameIsNullOrCityIsNull(name, city);
}
}
Related
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.
I am developping a chat app. I have two Entities
#Entity(tableName = "messages")
public class MessageItem {
#PrimaryKey
private Integer msgId;
#ColumnInfo(name = "from_id")
private String contact_id;
}
And
#Entity(tableName = "contact")
public class Contact{
#PrimaryKey
private Integer id;
#ColumnInfo(name = "contact_phone")
private String phone;
}
In MessageDao I want to get Contact phone correponding to the contact_id in MessageItem
You have three ways you can do this.
1) You can use a POJO with an #Embedded and an #Relation in which case you return MessageItem's with the Contact e.g. :-
public class MessageItemWithContact {
#Embedded
MessageItem messageItem;
#Relation(entity = Contact.class, parentColumn = "from_id", entityColumn = "id")
Contact contact;
}
along with an #Query such as :-
#Transaction
#Query("SELECT * FROM messages WHERE msgId=:msgId")
MessageItemWithContact getMessageByIdWithContact(int msgId);
2) Or you can use a POJO with an #Embedded and an additional variable for the phone using a JOIN e.g. :-
public class MessageItemWithPhone {
#Embedded
MessageItem messageItem;
String phone;
}
Along with an #Query like :-
#Query("SELECT msgId, contact_phone, from_id FROM messages JOIN contact On from_id = id ")
List<MessageItemWithPhone> getMessItemWithContactPhone();
doesn't need #Transaction as the query is a single transaction (whilst the previous method Room obtains the MessageItem and then builds a query to get the related object(s)).
this query gets all MessageItems (as no WHERE clause has been included)
3) Just get the phone using the from_Id for the relevant MessageItem without the need for a POJO by having an #Query like:-
#Query("SELECT contact_phone FROM contact WHERE id=:from_Id")
String getPhoneById(String from_Id);
I have an entity table, which does not associate to another two tables. Now I have a requirement to include also two columns, one from each of these tables, together with my entity in a response/dto.
As there is no association between the tables, I do not want to add the joins in my entity but just a special query. Is there a clean way to do so?
e.g.
#Entity
public class MyEntity {
#Id private Integer id;
#Column private String name;
#Column private String other;
}
public interface MyEntityRepo extends Repository<MyEntity, Integer> {
#Query("select a.info, b.desc, m.id, m.name, m.other from MyEntity m join TableA a on m.name = a.name join TableB b on m.name = b.name order by m.id")
List<MyEntityPlus> findAllPlus();
}
I tried using projection to create an interface MyEntityPlus like below, but it does not seem to work.
public interface MyEntityPlus {
String getInfo(); // from TableA
String getDesc(); // from TableB
Integer getId(); // from MyEntity
String getName(); // from MyEntity
String getOther(); // from MyEntity
}
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"
)
})
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.