How to access Object.field in criteria in Hibernate - java

i have write the criteria for company class.
below are company class, companySearch class and criteria. But criteria list is throw exception. exception is "org.hibernate.QueryException: could not resolve property: san.san of: com.sesami.common.domain.Company". How to access Company.san.san?
Company class
public class Company extends DomainObject implements UserDetails {
private Long id;
private String companyName;
private CompanyType companyType;
private String description;
private String companyURL;
private String billToEmail;
private String hashPassword;
private SAN san;
#OneToOne(cascade = { CascadeType.ALL })
public SAN getSan() {
return san;
}
public void setSan(SAN san) {
this.san = san;
}
...
}
CompanySearch
public class CompanySearch {
private String companyName;
private String email;
private Long san;
private String gstNumber;
......
public Long getSan() {
return san;
}
public void setSan(Long san) {
this.san = san;
}
...
}
Criteria
companyCriteria = this.getSession().createCriteria(
Company.class);
if (companySearch.getSan() != null
&& !"".equals(companySearch.getSan()))
companyCriteria.add(Restrictions.eq("san.san",
companySearch.getSan()));
Integer count = ((Long) companyCriteria.setProjection(
Projections.rowCount()).uniqueResult()).intValue();
companyCriteria.setProjection(null);
companyCriteria.setResultTransformer(Criteria.ROOT_ENTITY);
companyCriteria
.setFirstResult((pager.getPage() - 1) * pager.getPageSize())
.setMaxResults(pager.getPageSize()).list();
List<Company> companies = companyCriteria.list();
PagedResultSet pr = new PagedResultSet();
pr.setPager(pager);
pr.setResultSet(companies);
pr.setRowCount(count);
return pr;

You must create a join to the San entity, using a subcriteria, or an alias:
companyCriteria.createAlias("san", "sanAlias");
companyCriteria.add(Restrictions.eq("sanAlias.san",
companySearch.getSan()));
or
companyCriteria.createCriteria("san").add(Restrictions.eq("san",
companySearch.getSan()));
This is well explained in the Hibernate reference documentation and even in the Criteria javadoc.
Note that this has absolutely nothing to do with Spring, and everything to do with Hibernate. If you searched in the Spring doc for how to do this, no wonder you didn't find anything.

Related

How work with immutable object in mongodb and lombook without #BsonDiscriminator

I tried to work with immutable objects in MongoDB and Lombok. I found a solution to my problem but it needs to write additional code from docs but I need to used Bson annotations and create a constructor where describes fields via annotations. But if I user #AllArgsConstructor catch exception: "Cannot find a public constructor for 'User'" because I can't use default constructor with final fields. I think i can customize CodecRegistry correctly and the example will work correctly but I couldn't find solution for it in docs and google and Stackoverflow.
Is there a way to solve this problem?
#Data
#Builder(builderClassName = "Builder")
#Value
#BsonDiscriminator
public class User {
private final ObjectId id;
private final String name;
private final String pass;
private final String login;
private final Role role;
#BsonCreator
public User(#BsonProperty("id") final ObjectId id,
#BsonProperty("name") final String name,
#BsonProperty("pass") final String pass,
#BsonProperty("login") final String login,
#BsonProperty("role") final Role role) {
this.id = id;
this.name = name;
this.pass = pass;
this.login = login;
this.role = role;
}
#AllArgsConstructor
public enum Role {
USER("USER"),
ADMIN("ADMIN"),
GUEST("GUEST");
#Getter
private String value;
}
public static class Builder {
}
}
Example for MongoDB where I create, save and then update users:
public class ExampleMongoDB {
public static void main(String[] args) {
final MongoClient mongoClient = MongoClients.create();
final MongoDatabase database = mongoClient.getDatabase("db");
database.drop();
final CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()));
final MongoCollection<User> users = database.getCollection("users", User.class).withCodecRegistry(pojoCodecRegistry);
users.insertMany(new ExampleMongoDB().getRandomUsers());
System.out.println("Before updating:");
users.find(new Document("role", "ADMIN")).iterator().forEachRemaining(
System.out::println
);
System.out.println("After updating:");
users.updateMany(eq("role", "ADMIN"), set("role", "GUEST"));
users.find(new Document("role", "GUEST")).iterator().forEachRemaining(
System.out::println
);
}
public List<User> getRandomUsers() {
final ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 15; i++) {
users.add(
User.builder()
.login("log" + i)
.name("name" + i)
.pass("pass" + i)
.role(
(i % 2 == 0) ? User.Role.ADMIN : User.Role.USER
).build()
);
}
return users;
}
}
This should work (it worked for me):
#Builder(builderClassName = "Builder")
#Value
#AllArgsConstructor(onConstructor = #__(#BsonCreator))
#BsonDiscriminator
public class User {
#BsonId
private final ObjectId _id;
#BsonProperty("name")
private final String name;
#BsonProperty("pass")
private final String pass;
#BsonProperty("login")
private final String login;
#BsonProperty("role")
private final Role role;
}
Then in lombok.config add these (in your module/project directory):
lombok.addLombokGeneratedAnnotation=true
lombok.anyConstructor.addConstructorProperties=true
lombok.copyableAnnotations += org.bson.codecs.pojo.annotations.BsonProperty
lombok.copyableAnnotations += org.bson.codecs.pojo.annotations.BsonId
Also piece of advice, keep _id if you are going to use automatic conversion to POJOs using PojoCodec, which will save a lot of trouble.

Hot to add a list of parameters of another object in jpa Criteria

I have a class Director
public class Director {
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="transacao_id")
private Company company;
private Date registrationDate;
...
}
I have a class Company
public class Company {
private Long id;
private String cnpj;
private String description;
...
}
I need to add another filter in predicates...
I need to fetch a list from Director, filtering through a list of cnpj using jpa predicates, for example:
private Predicate[] criarRestricoes(FilterDTO filter, CriteriaBuilder builder, Root<Director> root) {
List<Predicate> predicates = new ArrayList<>();
Date today = new Date();
if (filter.getRegistrationDate() != null) {
predicates.add(builder.between(root.get("registrationDate").as(Date.class), today, filter.dateParam));
}
if (!StringUtils.isEmpty(filter.getCnpj())) {
predicates.add(builder.equal(???????????, ???????????????);
}
return predicates.toArray(new Predicate[predicates.size()]);
}
builder.equal(root.join("company").get("cnpj"), filter.getCnpj());

Spring Data Neo4j Parent Child filtering

I'm having a node relationship like above diagram
my Classes
#NodeEntity
public class User
{
#GraphId
private Long id;
#Property(name="name")
private String name;
#Relationship(type = "CAN_ACCESS", direction = Relationship.OUTGOING)
private List<Library> libraries;
// is this necessary ?????
#Relationship(type = "CAN_ACCESS", direction = Relationship.OUTGOING)
private List<Book> books;
public User()
{
}
// getters
// setters
}
#NodeEntity
public class Library
{
#GraphId
private Long id;
#Property(name="name")
private String name;
#Relationship(type = "CONTAINS", direction = Relationship.OUTGOING)
private List<Book> books;
// is this necessary ?????
#Relationship(type = "CAN_ACCESS", direction = Relationship.INCOMING)
private List<User> users;
public Library()
{
}
// getters
// setters
}
#NodeEntity
public class Book
{
#GraphId
private Long id;
#Property(name="name")
private String name;
// is this necessary ?????
#Relationship(type = "CONTAINS", direction = Relationship.INCOMING)
private Library library;
// is this necessary ?????
#Relationship(type = "CAN_ACCESS", direction = Relationship.INCOMING)
private List<User> users;
public Book()
{
}
// getters
// setters
}
I have User node Id = 21 and Library node Id = 32.I want to query Books that belongs to Library 32 but only User 21 can access.
Note - although User 21 "CAN_ACCESS" Library 32, that does not mean he "CAN_ACCESS" all Books "CONTAINS" in Library 32
My current approach in my service class is
#Autowired
private LibraryRepository libraryRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private BookRepository bookRepository;
#Autowired
private Session session;
public void testGraph()
{
Long userId = 21;
Long libId = 32;
int depth = 1;
Library library = libraryRepository.findOne(32,depth);
List<Book> books = library.getBooks();
List<Book> userAccessibleBooks = getUserAccessibleBooks(books,userId);
}
public List<Book> getUserAccessibleBooks(List<Book> books,Long userId)
{
// Loop over book list and get List<User> users;
// check this user "CAN_ACCESS" the book
// return accessible book list
}
I don't think this is the best approach. Assuming you have millions of Books in Library
Any solution ? or am i missing something in Spring data neo4j (Filters) ?
Summary
I want to get Library 32 with filtered Book List as children which User 21 "CAN_ACCESS"
You want to use repository finder for this kinds of queries. You want to return List<Book> so this will go into BookRepository.
Easiest way is to define a cypher query:
MATCH (u:User), (l:Library)
WHERE
ID(u) = {userId} AND ID(u) = {libraryId}
MATCH
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b
Using native graph ids is not really recommended, so if you introduce e.g. uuid the query might look like this:
MATCH
(u:User {uuid:{userUuid}}),
(l:Library {uuid:{libraryUuid}}),
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b
Then add a finder method with this query into BookRepository interface:
#Query("MATCH
(u:User {uuid:{userUuid}}),
(l:Library {uuid:{libraryUuid}}),
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b")
List<Book> findBooksByUserAndLibrary(#Param("userUuid") String userUuid, #Param("libraryUuid) String libraryUuid);

JPA/Hibernate doing update in table without any command

Well, I work with JSF + Hibernate/JPA, I have the following code:
public void loadAddressByZipCode(ActionEvent event){
Address address = boDefault.findByNamedQuery(Address.FindByZipCode, 'zipCode', bean.getAddress().getZipCode());
if (address == null){
//This zip code not exists in db
}else{
bean.setAddress(address);
}
}
This method above is called in every "onBlur" in inputText component in XHTML page, this inputText have the property value like this: "#{addressMB.bean.address.zipCode}"
So, when user (in XHTML page) type a new Zip Code this value is setted in "bean.getAddress().setZipCode()" and i search for this value in Database. But debugging my application i discovered that when i do "bean.getAddress().getZipCode()" the Hibernate launch a "update address set ..." in my DataBase. How can i prevent this and why this happens ?
EDIT 1:
This is my real method implemented
public void carregarLogradouroByCep(AjaxBehaviorEvent event) {
List listReturn = getBoPadrao().findByNamedQuery(
Logradouro.FIND_BY_CEP,
new NamedParams("cep", bean.getLogradouro().getCep()));
if (listReturn.size() > 0) {
Logradouro logradouro = (Logradouro) listReturn.get(0);
bean.setLogradouro(logradouro);
}else{
bean.setLogradouro(new Logradouro());
}
}
This is my component with onblur event:
<p:inputText value="#{enderecoMB.bean.logradouro.cep}" id="cep"
required="true" requiredMessage="O CEP é obrigatório">
<p:ajax event="blur"
listener="#{enderecoMB.carregarLogradouroByCep}"
update=":formManterEndereco:logradouro, :formManterEndereco:cidade, :formManterEndereco:estado,
:formManterEndereco:bairro" />
</p:inputText>
I have a "BasicDAOImpl" that make all operations in database, so tried do it in my entityManager:
#PostConstruct
private void init(){
entityManager.setFlushMode(FlushModeType.COMMIT);
}
But "update automatically" continue.
EDIT 2: My FIND_BY_CEP query
"SELECT c FROM Endereco c JOIN FETCH c.tipoEndereco JOIN FETCH c.logradouro"
EDit 3: My Entities
Endereco.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Endereco.findAllCompleto", query = "SELECT c FROM Endereco c "
+ "JOIN FETCH c.tipoEndereco " + "JOIN FETCH c.logradouro") })
#Table(name = "endereco")
public class Endereco extends AbstractBean {
/**
*
*/
private static final long serialVersionUID = 5239354646908722819L;
public Endereco(){
logradouro = new Logradouro();
}
#Transient
public static final String FIND_ALL_COMPLETO = "Endereco.findAllCompleto";
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_tipo_endereco")
private TipoEndereco tipoEndereco;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_logradouro")
private Logradouro logradouro;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_pessoa")
private Pessoa pessoa;
#Column
private String numero;
#Column
private String complemento;
public Logradouro getLogradouro() {
return logradouro;
}
public void setLogradouro(Logradouro logradouro) {
this.logradouro = logradouro;
}
public TipoEndereco getTipoEndereco() {
return tipoEndereco;
}
public void setTipoEndereco(TipoEndereco tipoEndereco) {
this.tipoEndereco = tipoEndereco;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public String getComplemento() {
return complemento;
}
public void setComplemento(String complemento) {
this.complemento = complemento;
}
public Pessoa getPessoa() {
return pessoa;
}
public void setPessoa(Pessoa pessoa) {
this.pessoa = pessoa;
}
}
Logradouro.java
#Entity
#NamedQueries(value = { #NamedQuery(name = "Logradouro.findByCep", query = "SELECT c FROM Logradouro c "
+ "JOIN FETCH c.bairro "
+ "JOIN FETCH c.cidade "
+ "WHERE c.cep = :cep "
+ "ORDER BY c.logradouro") })
#Table(name = "logradouro")
public class Logradouro extends AbstractBean {
public Logradouro(){
this.cidade = new Cidade();
this.bairro = new Bairro();
}
/**
*
*/
private static final long serialVersionUID = 1L;
#Transient
public static final String FIND_BY_CEP = "Logradouro.findByCep";
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_cidade")
private Cidade cidade;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name = "id_bairro")
private Bairro bairro;
#Column
private String cep;
#Column
private String logradouro;
public Cidade getCidade() {
return cidade;
}
public void setCidade(Cidade cidade) {
this.cidade = cidade;
}
public Bairro getBairro() {
return bairro;
}
public void setBairro(Bairro bairro) {
this.bairro = bairro;
}
public String getCep() {
return cep;
}
public void setCep(String cep) {
this.cep = cep;
}
public String getLogradouro() {
return logradouro;
}
public void setLogradouro(String logradouro) {
this.logradouro = logradouro;
}
}
Hibernate uses an entitymanager to do automatic handling of your entities by default. Your bean is one such entity so whenever the entitymanager feels it's time to flush to the database it does so. If you want to flush manually, you'll want to have a look at such a construction:
entityManager.setFlushMode(javax.persistence.FlushModeType.MANUAL);
I think that you misuse Hibernate. The main idea of ORM that is allows you to write code, ignoring that fact, that something goes into database. Your domain model must be completetly testable without any database. ORM is not abstraction to manage with relation database, it is abstraction for your object model. If you need to explicitly do inserts and updates, you better use some active record implementation or look at lightweight mapper like MyBatis.
And why you want to prevent database update? Property is changed, right? So let Hibernate decide, whether and when its new value must be written to database.

hibernate returned list casting issue

Two entity classes:
#Entity
public class FilesInfo {
#Id
#GeneratedValue
private Integer id;
private String name;
private String url;
#OneToMany(cascade= CascadeType.ALL)
#JoinColumn(name="fileId")
private Collection<FilesShare> filesShared = new ArrayList<FilesShare>();
public Collection<FilesShare> getFilesShared() {
return filesShared;
}
public void setFilesShared(Collection<FilesShare> filesShared) {
this.filesShared = filesShared;
}
the second:
#Entity
public class FilesShare {
#Id
#GeneratedValue
private Integer id;
#Column(name="fileId")
private Integer fileId;
private int userId;
private int owner;
#ManyToOne
#JoinColumn(name="fileId", updatable=false,insertable=false)
private FilesInfo filesInfo;
public FilesInfo getFilesInfo() {
return filesInfo;
}
public void setFilesInfo(FilesInfo filesInfo) {
this.filesInfo = filesInfo;
}
the daoimpl mmethod:
public List<FilesInfo> reqSharedFiles(FilesShare fs) {
session=HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<FilesInfo> filesInfo = null;
filesInfo=(List<FilesInfo>)session.createQuery("from FilesInfo a, FilesShare b where a.id=b.fileId and b.userId=5 and b.owner=1").list();
now i dont understand when hibernate is mapping and returning the object by i am getting this error in action that it cant be casted :
Ljava.lang.Object; cannot be cast to app.domain.FilesInfo
The action
private List<FilesInfo> furls= new ArrayList<FilesInfo>();
furls=upload.reqSharedFiles(fs);
for(FilesInfo s: furls) // ERROR IS SHOWING HERE
{
System.out.println(s.getUrl());
}
Your query does not return a list of FilesInfo, but a list of two-element object arrays where the objects are of type FilesInfo and FilesShare.
You could either rewrite your query so that it only returns a FilesInfo, or attach a ResultTransformer that pulls out the part you're interested in or constructs an object that you can use to get the info you really want.
Your query can result in two diffrent objects, from FilesInfo a, FilesShare b so if it is a FileShare object it cannot be cast to a FilesInfo
see hql from
Thankyou all. Here is what i did after some search.
private List<Object[]> turls=new ArrayList<Object[]>(); //get the list of Object[] from HQL
for(Object[] r : turls)
{
FilesInfo inf =(FilesInfo)r[0];
System.out.println(inf.getUrl());
}

Categories