spring data jpa find all by example nested collection property - java

I have two objects. The company that can have multiple nested addresses.
#Entity
#Data
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "phone")
private String phone;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Address> addresses;
}
Address class looks like this:
#Data
#Entity
#Table(name = "address")
#ToString(exclude = "company")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "postal_code")
private String postalCode;
#Column(name = "city")
private String city;
#Column(name = "street")
private String street;
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
}
I want somehow if it's possible, make a dynamic query that searches through the nested collection property. I made a search method which uses example matcher but the result is wrong. Every time I got everything from DB, not only company with address postal code that I'm looking for.
My search method looks like this:
#PostMapping("/search")
public List<Company> search(#RequestBody final Company company){
return companyRepository.findAll(Example.of(company,
ExampleMatcher.matchingAny()
.withIgnoreNullValues()
.withIgnorePaths("id")
.withStringMatcher(ExampleMatcher.StringMatcher.STARTING)));
}
In my database, I have two objects and this is the result of the search:
As you can see I received everything from DB instead of the only first company which address postal code starts with 1.

Hi you can use Specification<T>
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
For this you need to extend from interface JpaSpecificationExecutor:
public interface UserRepository extends JpaRepository<User> ,JpaSpecificationExecutor<User>{
}
And you also need to implement your custom Specification<T>
And then you can use repository.findAll(your impleneted Specification);
Spring docs :
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html
I think this is helpful.

Related

Address Entity in multiple Entities without specifying it

i am currently building an Application with Spring and i have a Question there:
I want to have an Entity Address which looks like this:
#Entity(name = "Address")
#Table(name = "address")
#EntityListeners(AuditingEntityListener.class)
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
#Column(name = "postalcode")
private String postalCode;
#Column(name = "state")
private String state;
#Column(name = "street")
private String street;
public Address() {
}
}
I want to use this Address Entity in multiple Entities, for example in the User or Order Entity. Later, i will like to have many Entities which need an Address. But i don't want to specify each Relation in the Address Entity, otherwise it will get to complex. Is it possible to have a Link from the User to the Address with only specifying this Link in the User Entity?
My User Entity looks something like this:
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
mappedBy = "address",
orphanRemoval = true,
cascade = {
CascadeType.PERSIST,
CascadeType.REMOVE
}
)
private Address billingAddress;
public User() {
}
}
Yes, it is possible, but you don't actually need mappedBy property, otherwise you are telling JPA to search for a address property in the other side of the relationship (that you actually want to be unidirectional):
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
private Address billingAddress;
public User() {
}
}
You can read more about this in the following online resources:
https://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html
https://www.baeldung.com/jpa-one-to-one
https://javabydeveloper.com/one-one-unidirectional-association/

Attribute many ids to single from other tables JPA

I want to create entity USERS_GROUP to link some user_ids to group_id.
I guess I overthink this case and now I can't find a solution.
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String userId;
private String name;
#OneToMany(mappedBy = "user")
private List<UserGroups> userGroups;
#Table(name = "groups")
#Data
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private String groupId;
private String category;
private String name;
private String description;
#OneToMany(mappedBy = "group")
private List<UserGroups> userGroups;
#Entity
#Data
#Table(name = "user_groups")
public class UserGroups {
#EmbeddedId
UserGroupsCompositeKey id;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
private Users user;
#ManyToOne
#MapsId("featureId")
#JoinColumn(name = "group_id")
private Group group;
#Embeddable
public class UserGroupsCompositeKey implements Serializable {
#Column(name = "user_id")
String userId;
#Column(name = "group_id")
String groupId;
}
I want to sent POST requests like "/group/{group_id}/users"
to send in request body some lists of user_ids to connect them.
I think I overconfigured this solution with a composite key.
Is there some easier and more readable solution for that case ?
EDIT: This solution is not working as I want it to do.
I create jpaRepository class with CompositeKey (Not sure if it's correct way )
#Repository
public interface ProductFeaturesRepository extends JpaRepository<UserGroups, UserGroupsCompositeKey> {
}
And I want to add some Controller method to group class to add some users to group with request body like:
Endpoint:/group/{group_id}/users
Body:
[
{
"userId": "USER-NR423423534634"
},
{
"userId": "USER-NR2355321"
}
]
It's not working properly nothing is added to database after that request

Join two tables with hibernate

I am looking to create a DAO which represents a join of two tables with Java Hibernate. Here is the SQL I'd like to represent (Postgres 9.6 incase that matters):
SELECT tableOneValue, tableTwoValue
FROM table_one, table_two
WHERE table_one_filter = 2 AND table_one_id = table_two_id;
These tables have a OneToOne relationship.
Table1.java
#Entity
#Data
#Table(name="table_one")
public class TableOneDao implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_one_id")
private int tableOneId;
#Column(name = "table_one_value")
private String tableOneValue;
#Column(name = "table_one_filter")
private int tableOneFilter;
}
Table2.java
#Entity
#Data
#Table(name="table_two")
public class TableTwoDao implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_twp_id")
private int tableTwpId;
#Column(name = "table_two_value")
private String tableTwoValue;
}
I'm very new to hibernate so maybe this isn't the right way to think with it. What I would love to do is define a SomeDao class where I can do: daoManager.findAll(SomeDao.class, Pair.of("tableOneFilter", 2));
This would return a List<SomeDao> where we get all the rows that satisfy tableOneFilter == 2.
You need to use the #OneToOne and #JoinColumn annotation.
Pay special attention to the userDetail attribute mapping.
For example, the user class:
#Entity
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "USR_ID")
private long id;
#Column(name = "USERNAME", nullable = false, unique = true)
private String username;
#Column(name = "PASSWORD")
private String password;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="USR_DET_ID")
private UserDetail userDetail;
// Add Constructor, Setter and Getter methods
}
And this user details class:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "USR_DET_ID")
private long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "EMAIL")
private String email;
#Column(name = "DBO")
private LocalDate dob;
// Add Constructor, Setter and Getter methods
}
Check the full code here.
Here is a JPA query which will work with your existing entity structure with the latest version of hibernate.
SELECT t1.tableOneValue, t2.tableTwoValue
FROM TableOneDao AS t1 JOIN TableTwoDao AS t2 ON t1.table_one_id = t2.table_two_id
WHERE t1.table_one_filter = ?
You can write a JPQL statement which is much better. Here is the sample solution:
SELECT NEW com.test.package.dao(t1.valueOne, t2.valueTwo)
FROM table_one t1 JOIN table_two t2
WHERE t1.filter = 2 AND t1.id = t2.id;
Please refer to this link and jump to the section where it mentions Result Classes (Constructor Expressions). Hope it helps. Thanks.

Jackson #JsonManagedReference on collection (one to many)

I have the following two classes.
School has many TestTakers
#Entity
#Table(name = "school")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class School extends BaseModel {
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "school")
// #JsonManagedReference <<<<< If not commented out, then error
private Set<TestTaker> testTakers;
//getter setters
}
// TestTaker.java
#Entity
#Table(name = "test_taker")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class TestTaker extends BaseModel {
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#ManyToOne
#JoinColumn(name = "school_id")
#JsonBackReference("school_testTaker")
private School school;
//getters setters
}
Can anyone explain why #JsonManagedReference cannot be annotated on a collection? I would get an error saying Jackson cannot handle managed/back reference. How exactly does #JsonManagedReference work with #JsonBackReference in common DB relationships One-to-one, One-to-many, Many-to-one
I have read the documentation, still don't quite understand what Jackson is trying to achieve

POST a Complex JSON using Javax-RS & Java Persistence & RESTful

I would like to apologize in advanced if this is a duplicate, however, I've been looking around for the last 2 days and have not found anything that solves my problem.
I have created a web service to which I would like to POST a JSON object. My issue is the following:
Let's say I have three objects.
ObjectA:{
"name":"",
"address":"",
"id":""
}
ObjectB:{
"id:"",
"name":"",
"objectA":{ [ObjectA]}
}
ObjectC:{
"id:"",
"name":"",
"objectA":{},
"objectB":{}
}
As you can see,ObjectC references ObjectA and ObjectB, which also references ObjectA. When inserting a new ObjectC, ObjectC.objectA should be the same as ObjectC.objectB.objectA.
The POST is consumed by the following method:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
super.create(entity);
}
The classes look like this: (plus the getters and setters)
#Entity
#Table(name = "object_a")
public class ObjectA{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#NotNull
#Column(name = "u_name")
private String uName;
#Column(name = "address")
private String address;
}
#Entity
#Table(name = "object_b")
public class ObjectB{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
}
#Entity
#Table(name = "object_c")
public class ObjectC{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
#JoinColumn(name = "object_b", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectB objectB;
}
Note: I am able to POST an ObjectA, and an ObjectB without any problem.
PROBLEM
The problem is that ObjectC.objectA and ObjectC.objectB.objectA are being inserted (or attempted to) as different values, which in throws an exception because ObjectA.uName is unique. If this wasn't the case, two new ObjectA's would have been created.
I was able to solve this problem in Hibernate, by doing something like the following:
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
ObjectC c = new ObjectC();
b.setObjectA(a);
c.setObjectA(a);
c.setObjectB(b);
session.beginTransaction();
session.save(a);
session.save(b);
session.save(c);
session.getTransaction().commit();
How can I go about this?
For anyone out there who might come across this issue, I have found the solution.
I did it in a very similar way to what I did in hibernate (see above).
I edited the create method I posted above, to this:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
if(entity.getObjectA().getId()==null){//If objectA doesn't have an ID, it must be new
entity.getObjectB().setObjectA(entity.getObjectA());
em.persist(entity.getObjectA());
em.persist(entity.getObjectB());
em.persist(entity);
}else{//Otherwise, everything may be inserted at once.
super.create(entity);
}
}
By the way, the super.create(entity) method contains the following:
public void create(T entity){
getEntityManager().persist(entity);
}

Categories

Resources