How to use AND in hibernate 5.2 Criteria? - java

I have two objects User and House. One user can have several houses.
Annotations from House.class:
#Entity
#Table(name="house")
public class House {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="house_id")
private int id;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
#Column(name="country")
private String country;
#Column(name="city")
private String city;
#Column(name="street")
private String street;
#Column(name="post_code")
private String postCode;
#Column(name="house_number")
private String houseNumber;
#Column(name="flats")
private int flats;
#Column(name="picture")
private String picture;
#Column(name="surname_first") // order names will be written in receipt
private Boolean writeSurnameFirst;
#Column(name="cut_name") // Nado ili net sokrashat imena
private Boolean cutName;
#ManyToOne
#JoinColumn(name="currency_id")
private Currency currency;
#ManyToOne
#JoinColumn(name="documentation_lang_id")
private Languages documentationLang;
#Column(name="default_house")
private Boolean defaultHouse;
I need to get a House object based on User and column defaultHouse = true.
I tried this code, but I can't get how to implement User into it:
tx = sess.beginTransaction();
// create criteria builder
CriteriaBuilder builder = sess.getCriteriaBuilder();
// create criteria
CriteriaQuery<House> query = builder.createQuery(House.class);
// specify criteria root
Root<House> root = query.from(House.class);
query.select(root).where(builder.equal(root.get("default_house"), true)
.and(builder.equal(root.get(House.getUser), user)));
house = sess.createQuery(query).getSingleResult();
tx.commit();

In fact builder.and() takes two parameters which are the two restrictions to be joined with AND keyword in SQL, and the builder.and() should be used inside builder.where() method.
change the following code:
query.select(root).where(builder.equal(root.get("default_house"), true)
.and(builder.equal(root.get(House.getUser), user)));
Like this:
query.select(root).where(builder.and(builder.equal(root.get("default_house"), true), builder.equal(root.get(House.getUser), user))));
Please refer to Hibernate ORM 5.2.11.Final User Guide for further details and more examples.
Note:
Hibernate is concerned about objects and not tables and when we use its modules like Criteria we use attributes names in the Object and not DB columns names.

In CriteriaBuilder methods you must use the entity field names like following:
builder.equal(root.get("defaultHouse"), true)
and not the database column names:
builder.equal(root.get("default_house"), true)

I attached my example:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Eje> query = builder.createQuery(Eje.class);
Root<Eje> productRoot = query.from(Eje.class);
query.select(productRoot)
.where(builder.and(builder.equal(productRoot.get("id"), 1), builder.equal(productRoot.get(Eje_.factorCriticos).get(FactorCritico_.id), 1)));
result =(List<T>) session.createQuery(query).getResultList();

Related

Create a new JPA Entity to produce a new object from two tables

Hello programming council, this is my first use of JPA in anger.
I have 2 Tables:
Entity
#Table(name="category")
public class Category {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="category")
private String category;
#Column(name="budget")
private double budget;
#Column(name="savings")
private String savings;
#Column(name="archive")
private String archive;
Entity
#Table(name="Transaction")
public class Transaction {
#Id
#Column(name="transaction_no")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long transactionNo;
#Column(name="transaction_date")
private String transactionDate;
#Column(name="transaction_category")
private String transactionCategory;
#Column(name="transaction_description")
private String transactionDescription;
#Column(name="transaction_amount")
private double transcationAmount;
#Column(name="transaction_auto")
private String transactionAuto;
I want to create a new object called Tile which will contain String category and String balance, the SQL for which would be:
select t.transaction_category as category, sum(t.transaction_amount) as balance
from budgeteer.category c
join budgeteer.transaction t
on c.category = t.transaction_category
group by t.transaction_category;
What is the easiest/best way for me to accomplish this?
Thanks in advance.
Ok, so after a little more research, I discovered that I could actually just do this with the same Entity, repository and service without generating a table. You just need to leave out the #Table annotation when you create your entity.

What is the most efficient way to retrieve specific data from DB without doing loop for all records?

I want to ask about what is the most efficient way to search about specific data from a database without doing a for loop in all of the records?
I have a project on java spring and I have this Entity:
#Entity
#Table(name = "USERS") public class USERS {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "UID")
private Integer id;
#Column(name = "FName")
private String firstName;
#Column(name = "SName")
private String secondName;
#Column(name = "TName")
private String thirdName;
#Column(name = "LName")
private String fourthName;
#Column(name = "Email")
private String email;
#Column(name = "PW")
private String password;
#Column(name = "MNumber")
private String mobileNumber;
#Column(name = "ISDeleted")
private boolean isUserDeleted;
//---------------------- Getters and Setters ----------------------
and I made this service:
public List<USERS> findAllActive() {
List<USERS> usersList = new ArrayList<USERS>();
for (USERS users: usersRepository.findAll()){
if (!users.isUserDeleted()){
usersList.add(users);
}
}
return usersList;
}
For example; I have one property for User, if he is active or not.
So, my question; what is the most efficient way to do get specific data like retrieving all of the active users from the DB without doing a for loop like in the code above? Because if the list of users is a 1 Million or more, it could have performance issues.
Assuming that you are using JpaRepository then you can create custom query.
#Query("SELECT u FROM USERS u WHERE u.userDeleted = false")
List<USERS> findNotDeletedUsers();
and then call usersRepository.findNotDeletedUsers();
First of all, use an index on the field you want to search on (this won't help you much if the column has only two distinct values, but will make a huge difference if the value has high sparsity).
#Entity
#Table(name = "USERS",
indexes = {
// not a huge performance gain, since the column values are true/false
#Index(name = "index_by_active", columnList="ISDeleted", unique = false),
// possible huge performance gain, since only the relevant records are scanned
#Index(name = "index_by_first_name", columnList="FName", unique = false)})
public class USERS {...}
Then, define a query method that uses the indexed field (if you are using spring data it would look as follows).
public interface UsersRepository extends CrudRepository<USERS, Long> {
List<USERS> findUsersByISDeleted(boolean deleted);
List<USERS> findUsersByFName(String name);
List<USERS> findUsersByFNameAndISDeleted(String name, boolean deleted);
}
Queries on indexed fields will leverage the underlying index and provide an efficient access plan (so you won't end up scanning the whole table in order to extract a subset of entities matching a given criteria).
The solution from #Madis is okay. But if you always want to get users which are not deleted in all queries, you can specify it on Entity:
#Entity
#Table(name = "USERS")
#Where("ISDeleted = false")
public class USERS {
So now the condition "ISDeleted = false" is automatically append to all queries from the UserRepository. You can use usersRepository.findAll() instead of.
You don't need to specify any sql query or where clause. CrudRepository will do it for you automatically. Just use below code and pass true/false on need basis
List<Users> findIsUserDeleted(boolean isDeleted)

JPA Criteria API Specification for Many to Many

I have got three classes as mentioned below. I am trying to create a specification to filter data where there is a match in the linked table.
public class Album {
private Long id;
private List<AlbumTag> albumTags;
}
public class Tag {
private Long id;
private String category;
}
public class AlbumTag{
private Long id;
private Album album;
private Tag tag;
}
In the schema given above what I am trying to find is a list of all albums from Album table with the link in AlbumTag. The SQL that I want to achieve, doesn't have to be same, is below
select *
from Album A
where (A.Id in (select [AT].AlbumId
from AlbumTag [AT]))
What I have tried so far which is not working, of course, is below
public class AlbumWithTagSpecification implements Specification<Album> {
#Override
public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
final Subquery<Long> personQuery = cq.subquery(Long.class);
final Root<Album> album = personQuery.from(Album.class);
final Join<Album, AlbumTag> albumTags = album.join("albumTags");
personQuery.select((albumTags.get("album")).get("id"));
personQuery.where(cb.equal(album.get("id"), (albumTags.get("album")).get("id")));
return cb.in(root.get("id")).value(personQuery);
}
}
Using spring boot and spring data JPA, you can prefer entity relationship to fetch the data.
1.Annotate the domain class with the entity relationship which given below:
#Entity
#Table(name="Album")
public class Album {
#Id
#Column(name="id")
private Long id;
#OneToMany(targetEntity = AlbumTag.class, mappedBy = "album")
private List<AlbumTag> albumTags;
//getter and setter
}
#Entity
#Table(name="Tag")
public class Tag {
#Id
#Column(name="id")
private Long id;
#Column(name="category")
private String category;
//getter and setter
}
#Entity
#Table(name="AlbumTag")
public class AlbumTag{
#Id
#Column(name="id")
private Long id;
#ManyToOne(optional = false, targetEntity = Album.class)
#JoinColumn(name = "id", referencedColumnName="id", insertable = false, updatable = false)
private Album album;
#ManyToOne(optional = false, targetEntity = Tag.class)
#JoinColumn(name = "id", referencedColumnName="id", insertable = false, updatable = false)
private Tag tag;
//getter and setter
}
2.use the spring data to fetch the details using the below:
Album album = ablumRepository.findOne(1); // get the complete details about individual album.
List<AlbumTag> albumTags = ablum.getAlbumTags(); // get the all related albumTags details for particular album.
I hope this will help you to solve it.
Subqueries in JPA only really work with CriteriaBuilder.exists() so i would try:
public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
final Subquery<Long> subQuery = cq.subquery(Long.class);
final Root<AlbumTag> albumTag = subQuery.from(AlbumTag.class);
// it doesn't really matter what we select
subQuery.select(cb.literal(1));
subQuery.where(cb.equal(root.get("id"), (albumTag.get("album")).get("id")));
return cb.exists(subQuery);
}
which is equivalent to
select *
from Album A
where exists(
select 1 from AlbumTag AT
where AT.AlbumId = A.Id
)
Well, I wouldn't go for in operation in this case - it just complicates the query and the specification. The problem you described is actually matter of joining records from Table A with related records from Table B so the query in your case would be like:
SELECT a from Album a join AlbumTag at on a.id = at.albumId - as you needed it will return all albums that have album tags. Inner join explained
So in your case I would create this "factory" method that would create for you this specification.
public static Specification<Album> withTags() {
return new Specification<Album>() {
#Override
public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return root.join("albumTags").getOn();
}
};
}
Also I would suggest you to have a look at static metamodel library from hibernate - link to introduction. It generates for you static model from your entity classes that helps you avoid creating queries/specifications using hardcoded strings.
creteria query for join tables
CriteriaQuery<Album> query = cb.createQuery(Album.class);
Root<Album> album = query.from(Teacher.class);
Join<Album, AlbumTag> tag = teacher.join("id");
query.select(tag).where(cb.equal(album.get("album")));
List<Album> results = em.createQuery(query).getResultList();
for (Album al : results) {
System.out.println("album-->+al.get(name));
}
This looks like a classic many to many example. The three classes you have map directly to the tables you would expect in the database. JPA is an Object Relational Mapping (ORM) library which means we can structure the classes in a more OO style and map to the underlying relational database.
The AlbumTag class can be omitted and the #ManyToMany relationship added to both Album and Tag.
public class Album {
private Long id;
#ManyToMany
#JoinTable(name="AlbumTag",
joinColumns=
#JoinColumn(name="album", referencedColumnName="id"),
inverseJoinColumns=
#JoinColumn(name="tag", referencedColumnName="id"))
private List<Tag> tags;
}
public class Tag {
private Long id;
private String category;
#ManyToMany(mappedBy="tags")
private List<Album> albums;
}
To find albums by Tag you would first retrieve the Tag from the repository using something like findById(1l); or findByCategory("Rock"); and then simply call getAlbums() on the Tag object.
Note: One slight difference here is that the AlbumTag table would have only two columns (album and tag). The extra id column on AlbumTag is unnecessary since the combination of album and tag would be a unique id and you would never need to find by id in this table anyway.
Since you are using spring-data-jpa you should really take advantage of the features it provides.
My first question is related to your entity classes. I do not understand why is it necesary to store a list of album tags in the album class. Since you have a join table this information is reduntant.
Secondly you should adnotate your entity clases:
#Entity
public class Album {
#Id
#Column
private Long id;
}
#Entity
public class Tag {
#Id
#Column
private Long id;
#Column
private String category;
}
#Entity
#Table
public class AlbumTag{
#Id
#Column
private Long id;
#ManyToOne
#JoinColumn
private Album album;
#ManyToOne
#JoinColumn
private Tag tag;
}
Next you should create repositories for your entity classes.
interface AlbumRepository extends JpaRepository<Album, Long>{
#Query
("select DISTINCT(a) from AlbumTag at "+
"join at.album a "
"where at.tag is not null")
List<Album> findAlbumWithTag();
}
Then simply call the repository function which will return a list of albums which have at least one tag.

Ebean Query by OneToMany Relationship

I'm using Ebean with the Play 2 Framework and got two models: a user model and a book model. The user model is connected with the book model in a OneToMany Relationship. So every user can have many books or no book at all. The book model itself has properties too. Now I want to create a query in the user model, which returns only users, who have books with certain properties. For example: One property might be condition, like new or used. Now give me all users which have books in new condition.
Is it possible to create such a query with the Ebean methods? Or do I have to use raw SQL?
Say you have the following models:
#Entity
public class User extends Model {
#Id
#Column(name = "user_index")
private int id;
#Column(name = "user_first_name")
private String firstName;
[...]
#OneToMany(mappedBy = "book_owner_index")
private List<Book> books;
public static Finder<Integer, User> find = new Finder<Integer, User>(Integer.class, User.class);
[...]
}
and
#Entity
public class Book extends Model {
#Id
#Column(name = "book_index")
private int id;
#Column(name = "book_name")
private String name;
#Column(name = "book_condition")
private String condition;
[...]
#ManyToOne
#JoinColumn(name = "book_owner_index", referencedColumnName = "user_index")
private User owner;
[...]
}
Then you can do a search like:
List<User> users = User.find.select("*")
.fetch("books")
.where()
.eq("books.condition", "new")
.findList();
List<User> users = User.find.select("*")
.fetch("books")
.where()
.eq("t1.condition", "new")
.findList();
For me, it works only when I use "t1.", I am using Postgres DB. The generated query makes sense with t1.

Hibernate Criteria select using embedded object (tuple)

In my case I have a SQL query which looks like:
select * from event_instance where (object_id, object_type) in
(<LIST OF TUPLES RETRIEVED FROM SUBQUERY>);
I want to map this on Hibernate Entities and I have a problem with this query. My mapping looks like that:
#Entity
#Table(name="event_instance")
public class AuditEvent {
<OTHER_FIELDS>
#Column( name = "object_type", nullable = false)
private String objectType;
#Column( name ="object_id" , nullable = false)
private Integer objectId;
}
and second entity:
#Entity
#Table(schema = "els" ,name = "acg_objects")
public class AcgObject implements Serializable{
#Id
#Column(name = "acg_id")
private String acgId;
#Id
#Column(name="object_type")
private String objectType;
#Id
#Column(name="object_id")
private Integer objectId;
<OTHER FIELDS>
}
I already run query for getting AcgObjects and for my DAO I'm getting List only thing I want to do is query a touple using criteria like:
crit.add(Restrictions.in("objectType,objectId",<List of tuples>);
Is it possible? I was trying to use #Embedded object but don't know how exactly construct a query for it. Please help
You can do that not in standard SQL nor using criteria; you have to split in two distinct restrictions or using a Session.SQLQuery() if you want to use specific RDBMS (look at SQL WHERE.. IN clause multiple columns for an explanation)

Categories