Spring specification with custom cross join by simple attribute - spring

Following scenario
#Entity("YEAR")
public class Year{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
#Column(name = "NAME", nullable = false, length = 10)
public Long name;
...
}
#Entity("FOO")
public class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
#Column(name = "FK_YEAR", nullable = false)
public Long yearId;
#Column(name = "NAME", nullable = false, length = 10)
public String name;
...
}
#Entity("FII")
public class Fii {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
#Column(name = "FK_YEAR", nullable = false)
public Long yearId;
#Column(name = "CODE", nullable = false, length = 10)
public String code;
...
}
#Entity("NTOM")
public class NtoM {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
#Column(name = "FK_FOO", nullable = false)
public Long fooId;
#Column(name = "FK_FII", nullable = false)
public Long fiiId;
#Column(name = "STATE", nullable = false)
public Boolean state;
#Column(name = "VALUES", length = 500)
public String values;
...
}
Resulting in an ERP like this:
I now do have a JpaRepository like this:
#Repository
public interface NtoMRepository extends JpaRepository<NtoM, Long>, JpaSpecificationExecutor<NtoM> {
String BASE_QUERY =
"SELECT"
// prevent jpa to return null instead of id=0
+ " CASE WHEN ntom.ID IS NULL THEN 0 ELSE ntom.ID END AS ID ,"
+ " CASE WHEN ntom.STATE IS NULL THEN 0 ELSE ntom.STATE END AS STATE ,"
+ " ntom.VALUES,"
+ " fii.ID AS FK_FII,"
+ " foo.ID AS FK_FOO "
+ " FROM MYSCHEMA.FOO foo"
+ " INNER JOIN MYSCHEMA.FII fii ON fii.FK_YEAR = foo.FK_YEAR"
+ " OUTER JOIN MYSCHEMA.NTOM ntom ON ntom.FK_FII = fii.ID AND ntom.FK_FOO = foo.ID"
#Query(value = BASE_QUERY + " WHERE fii.ID = :fiiId", nativeQuery = true)
List<Option> findByFiiId(#Param("fiiId") Long fiiId);
#Query(value = BASE_QUERY + " WHERE foo.ID = :fooId", nativeQuery = true)
List<Option> findByFooId(#Param("fooId") Long fooId);
}
So the 2 queries here compute me missing entries whenever I call them, which works out quite nicely.
How could I use the "toPredicate" of the https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/ to accomplish this by creating a similar behavior but with dynamic parameters?
I cant just use criteriabuilder "join" as the values are only "basic attributes". Also do I want to reuse the dynamic approach as the "controller endpoint can look like"
#GetMapping
public List<NtoM> find(#RequestParam(value = "id", required = false, defaultValue = "0") Long id,#RequestParam(value = "fiiId", required = false, defaultValue = "0") Long fiiId, #RequestParam(value = "fooId", required = false, defaultValue = "0") Long fooId){
Specification<NtoM> spec = ... //build as AND construct of all parameters (if not null or empty add it)
// TODO instead of the SELECT * FROM myschema.ntom the custom query here!
return repo.findAll(spec);
}
How can I do this. I can also use the EntityManager and the criteriaBuilder.createTupleQuery(). But it seems to not work (I cant join the tables as there is no "ManyToOne" between them)

Why aren't there relationships in your domain model? It can be handled efficiently if you change your model to have relationships. Here is how I would approach this problem:
Start with creating relationships.
#Entity("YEAR")
public class Year{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
#Column(name = "NAME", nullable = false, length = 10)
public Long name;
...
}
#Entity("FOO")
public class Foo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
//#Column(name = "FK_YEAR", nullable = false)
//public Long yearId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_YEAR", referencedColumnName = "ID")
public Year year;
#Column(name = "NAME", nullable = false, length = 10)
public String name;
...
}
#Entity("FII")
public class Fii {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
//#Column(name = "FK_YEAR", nullable = false)
//public Long yearId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_YEAR", referencedColumnName = "ID")
public Year year;
#Column(name = "CODE", nullable = false, length = 10)
public String code;
...
}
#Entity("NTOM")
public class NtoM {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
public Long id;
//#Column(name = "FK_FOO", nullable = false)
//public Long fooId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_FOO", referencedColumnName = "ID")
public Foo foo;
//#Column(name = "FK_FII", nullable = false)
//public Long fiiId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_FII", referencedColumnName = "ID")
public Fii fii;
#Column(name = "STATE", nullable = false)
public Boolean state;
#Column(name = "VALUES", length = 500)
public String values;
...
}
Change the controller to get the request parameters into map.
Then delegate the data retrieving logic to the service layer (You can also create custom repository impelementation).
#GetMapping
public List<NtoM> find(#RequestParam Map<String, String> requestParams) {
return service.findByRequestParams(requestParams);
}
In the service class
public List<NtoM> findByRequestParams(Map<String, String> requestParams) {
return repository.findAll(createSpec(requestParams));
}
private Specification<NtoM> createSpec(Map<String, String> requstParams ) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Join<NtoM, Fii> firstJoin = root.join("fii", JoinType.INNER);
Join<NtoM, Foo> secondJoin = fiiJoin("foo", JoinType.LEFT);
String value = requstParams.get("id");
if(StringUtils.isNotBlank(value)) {
Predicate id = criteriaBuilder.equal(secondJoin.get("id"), Long.parseLong(value));
predicates.add(id);
}
value = requestParams.get("fiiId");
if(StringUtils.isNotBlank(value)) {
Predicate fii = criteriaBuilder.equal(secondJoin.get("fii"), Long.parseLong(value));
predicates.add(fii);
}
value = requestParams.get("fooId");
if(StringUtils.isNotBlank(value)) {
Predicate foo = criteriaBuilder.equal(secondJoin.get("foo"), Long.parseLong(value));
predicates.add(foo);
}
//Later you can add new options without breaking the existing API
// For example like search by values
value = requestParams.get("values");
if(StringUtils.isNotBlank(value)) {
Predicate likeValues = criteriaBuilder.like(secondJoin.get("values"), "%" + value + "%");
predicates.add(likeValues);
}
return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
};
}

Related

Spring JPA Specification. Join query in order to find user

I have two tables:
#Table(name = "userinstance")
#Entity
public class UserInstance implements Comparable<UserInstance> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
private String uuid;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
String userId;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
private String name;
#NotNull
#Size(min = 1, max = 2048)
#Column(length = 2048, unique = false, nullable = false)
private String description;
#JsonBackReference
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userInstance")
private List<UserDetailsInstance> userDetailsInstances = new ArrayList<>();
}
#Table(name = "userdetailsinstance")
#Entity
public class UserDetailsInstance implements Comparable<UserDetailsInstance> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
private String uuid;
#JsonManagedReference
#ManyToOne
#JoinColumn(name = "usr_instance_id", nullable = false)
private UserInstance userinstance;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
private String name;
#NotNull
#Size(min = 1, max = 100)
#Column(length = 100, unique = false, nullable = false)
private String type;
#Column(length = 1)
private Status status;
#Column(length = 1000)
private String message;
#Column(length = 4000)
private String instanceData;
}
What I want to achieve is to be able to find the User (UserInstance table) using filter and using only single query parameter ("name");
In order to do that i have a:
public class UserInstanceQuery extends BaseQuery {
private final Long id;
private final String name;
private final String user;
private final Instant createdDate;
private final Instant lastModifiedDate;
public UserInstanceQuery(Long id, String name, String user, String createdDate, String lastModifiedDate, Pageable pageable) {
super(pageable);
this.id = id;
this.name = name;
this.user = user;
this.createdDate = convertStringToInstant(createdDate);
this.lastModifiedDate = convertStringToInstant(lastModifiedDate);
}
public Specification<UserInstance> toSpecification() {
return new UserSpecification(this);
}
}
And the specification looks like:
public class UserInstanceSpecification implements Specification<UserInstance> {
private final UserInstanceQuery userInstanceQuery;
#Override
public Predicate toPredicate(Root<UserInstance> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new LinkedList<>();
if (userInstanceQuery.getId() != null) {
predicates
.add(
cb.equal(root.get("id"),
userInstanceQuery.getId())
);
}
if (userInstanceQuery.getUser() != null && !userInstanceQuery.getUser().isEmpty()) {
predicates
.add(cb.equal(
cb.lower(root.get("userId")), userInstanceQuery.getUser().toLowerCase()));
}
if (userInstanceQuery.getName() != null && !userInstanceQuery.getName().isEmpty()) {
Subquery<UserInstance> subquery = query.subquery(UserInstance.class);
Root<UserDetailsInstance> subRoot = subquery.from(UserDetailsInstance.class);
List<Predicate> subPredicates = new ArrayList<>();
subPredicates.add(cb.like(subRoot.get("instanceData"), "%" + userInstanceQuery.getName() + "%"));
subquery.select(subRoot.get("userInstance")).where(subPredicates.toArray(new Predicate[predicates.size()]));
predicates.add(
cb.or(
cb.like(cb.lower(root.get("name")), "%" + userInstanceQuery.getName().toLowerCase() + "%"),
cb.like(cb.lower(root.get("environment")), "%" + userInstanceQuery.getName().toLowerCase() + "%"),
cb.like(cb.lower(root.get("description")), "%" + userInstanceQuery.getName().toLowerCase() + "%"),
cb.exists(subquery)
)
);
}
if (userInstanceQuery.getCreatedDate() != null && userInstanceQuery.getLastModifiedDate() != null) {
predicates.add(
cb.and(
cb.greaterThanOrEqualTo(root.get("createdDate"), userInstanceQuery.getCreatedDate()),
cb.lessThanOrEqualTo(root.get("lastModifiedDate"), userInstanceQuery.getLastModifiedDate())
)
);
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
private boolean currentQueryIsCountRecords(CriteriaQuery<?> criteriaQuery) {
return criteriaQuery.getResultType() == Long.class || criteriaQuery.getResultType() == long.class;
}
}
The issue is, that when i search for a string that contains only in UserDetailsInstance "instanceData" Field i get multiple results that don't actually have the data i need and are not referring to the user
Hibernate generated an sql like this:
select user0_.id as id1_7_, user0_.created_by as created_by2_7_, user0_.created_date as created_date3_7_, user0_.last_modified_by as last_modified_by4_7_, user0_.last_modified_date as last_modified_date5_7_, user0_.version as version6_7_, user0_.description as description7_7_, user0_.environment as environment8_7_, user0_.name as name9_7_, user0_.user_id as user_id10_7_, user0_.uuid as uuid11_7_ from usertinstance user0_
where (lower(user0_.name) like ?
or lower(user0_.environment) like ?
or lower(user0_.description) like ?
or exists (select userdetail.tds_instance_id
from userdetailsinstance userdetail
cross join usertinstance user2_
where userdetail.tds_instance_id=user2_.id
and (userdetail.instance_data like ?)))
and user0_.created_date>=? and user0_.last_modified_date<=? order by user0_.id desc fetch first ? rows only
If i try to run this query in SQLDeveloper/Datagrip I also get a multiple set of rows
At the same time if i change (line 8)
where userdetail.usr_instance_id=user2_.id
to
where userdetail.usr_instance_id=user0_.id
I get the result i need
Can you please point me to the error in Query creation or maybe tell me how to make this query return a value i need
Thanks!
The solution was much easier as i've expected
UserInstanceSpecification:
if (userInstanceQuery.getName() != null && !userInstanceQuery.getName().isEmpty()) {
Join<UserInstance, UserDetailsInstance> bos = root.join("userDetailsInstances", JoinType.LEFT);
predicates.add(
cb.or(
cb.like(cb.lower(root.get("name")), "%" + userInstanceQuery.getName().toLowerCase() + "%"),
cb.like(cb.lower(root.get("description")), "%" + userInstanceQuery.getName().toLowerCase() + "%"),
cb.like(cb.lower(bos.get("instanceData")), "%" + userInstanceQuery.getName().toLowerCase() + "%")
)
);
}

Why hibernate is throwing constraintViolationException?

Order Entity
#Entity
#Table(name = "Order",
indexes = {
#Index(name = "ORDER_X1", columnList = "REFERENCE_ID,SOURCE_ID"),
#Index(name = "ORDER_X2", columnList = "TYPE,STATUS")
}
)
#DiscriminatorColumn(name="PROCESSOR_TYPE", discriminatorType=DiscriminatorType.STRING, length = 80)
#SequenceGenerator(name="orderSeq", sequenceName="ORDER_SEQ")
#Inheritance(strategy= InheritanceType.JOINED)
public abstract class OrderEntity implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE, generator="orderSeq")
private Long id;
#ManyToMany(cascade={CascadeType.MERGE})
#JoinTable(
name = "FILE_ORDER_MAP",
joinColumns = {#JoinColumn(name = "ORDER_ID")},
inverseJoinColumns = {#JoinColumn(name = "FILE_ID")}
)
private Set<TransferFile> transferFiles = new HashSet<>();
#Column(name = "TYPE")
#Enumerated(EnumType.STRING)
private OrderType type;
#Column(name = "AMOUNT", precision = 12, scale = 2)
private LcMoney amount;
#Column(name = "STATUS")
#Enumerated(EnumType.STRING)
private OrderStatus reconStatus;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "STATUS_D", nullable = false)
#LcDateTimeUtc()
private DateTime reconStatusDate;
#Column(name = "REFERENCE_ID")
private Long referenceId;
#Column(name = "SOURCE_ID")
private Long sourceId;
#Column(name = "ACCOUNT_ID")
private Long accountId;
#Column(name = "PROCESSOR_TYPE", insertable = false, updatable = false)
#Enumerated(EnumType.STRING)
private OrderProcessorType processorType;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "TX_EXECUTION_D")
#LcDateTimeUtc()
private DateTime executedDate;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "CREATE_D")
#LcDateTimeUtc()
private DateTime createDate;
#Column(name = "IS_ON_DEMAND")
#Type(type = "yes_no")
private boolean isOnDemand;
#ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.PERSIST})
#JoinColumn(name="PAYER_ID", nullable=true)
private Payer payer;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID")
private List<OrderTransaction> orderTransactions;
#OneToMany(cascade = {CascadeType.ALL})
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID",
foreignKey = #ForeignKey(name = "FK_ORDER")
)
private List<MatchResult> matchResults;
#Version
#Column(name = "VERSION")
private Integer version;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "externalSourceId", column = #Column(name = "TRANS_EXT_SRC_ID")),
#AttributeOverride(name = "externalId", column = #Column(name = "TRANS_EXT_REF_ID"))
})
private LcExternalIdEntity transExtId;
#PreUpdate
#PrePersist
public void beforePersist() {
if (reconStatusDate != null) {
reconStatusDate = reconStatusDate.withZone(DateTimeZone.UTC);
}
if (executedDate != null) {
executedDate = executedDate.withZone(DateTimeZone.UTC);
}
if (createDate != null) {
createDate = createDate.withZone(DateTimeZone.UTC);
}
}
// getters and setters
}
//controller method
public Response processFile(){
// separate trasaction
service.readFileAndCreateOrders(); // read files and create orders in new status
List<Order> newOrders = service.getNewOrders();
for( Order order: newOrders){
service.processOrder(order); // separate transaction
}
}
#Transaction
void processOrder(OrderEntity order){
matchResultJpaRepository.save(orderEntity.id);
log.info("Saving matchId={} for order={}", match.getId(), order.getId());
// create new transaction and add to order
OrderTransaction transaction = createNewTransaction(order);
order.getTransactions().add(transaction);
order.setStatus("PROCESSED");
log.info("Saving Order id={}, Type={}, Status={} ", order.getId(), order.getType(), order.getStatus());
orderRepository.save(order);
}
I am seeing this below error.
ORA-01407: cannot update ("PAYMENTS"."MATCH_RESULT"."ORDER_ID") to NULL
This endpoing is not exposed to user. There is a batch job which invokes this endpoint.
This code has been there for atleast a year and this is the first time i am seeing this.
This happened only once and for only one call. I am seeing both the logs printed. I am puzzled why I am seeing above error complaining about NULL order id. From the logs, we can confirm that the order id is definitely not null.
Any idea why this is happening? What can be done to fix this?

Is there any way to delete entities from child entity using JPA?

I have 3 tables Table A, B, C. Table A is associated #OneToMany with Table B. Table B is associated #ManyToOne with Table C. Now when I find by Id of Table A, I am able to get details of A,B,C. But when I persist / delete, It is affecting only Table A&B. Table C is unaffected.
Is this possible in JPA to delete entities from child entity? Googled lot, but could not find any clue.
Below are the entity models of all the three tables
#Entity
#Table(name = "FEATUREMASTER")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class FeatureMaster implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "FGID")
private String featureid;
#Column(name = "FEATURENAME", nullable = false, unique = false)
private String featurename;
#Column(name = "DESCRIPTION", nullable = true, unique = false)
private String description;
#Column(name = "LIBNAME", nullable = true, unique = false)
private String libname;
#Column(name = "ISENABLED", nullable = false, unique = false)
private String isenabled;
#Column(name = "EDRULEGRP", nullable = true, unique = false)
private String edrulegrp;
// Do Not use - [orphanRemoval = true & CascadeType.ALL]- If used, deletion is not happening
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "FGID")
private List<CfgMaster> parameters;
// Getters and Setters
}
#Entity
#Table(name = "CFGMASTER")
public class CfgMaster implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#EmbeddedId
private CfgMasterPK id;
#Column(name = "CONFIGNAME", length = 45, nullable = true, unique = false)
private String parameter_name;
#Column(name = "CONFIGTYPE", length = 20, nullable = true, unique = false)
private String type;
#Column(name = "SUBPARAM", nullable = true, unique = false)
private Integer subparam;
#Column(name = "CONFIGDESCRIPTION", nullable = true, unique = false)
private String description;
#Column(name = "CONFIGLIMITFROM", nullable = true, unique = false)
private String from;
#Column(name = "CONFIGLIMITTO", nullable = true, unique = false)
private String to;
#ManyToOne(cascade = {CascadeType.ALL}, optional = true, fetch = FetchType.LAZY )
// #ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#NotFound(action=NotFoundAction.IGNORE) // This is required to handle when no CfgData is found
#JoinColumns({
#JoinColumn(name = "FGID", insertable = false, updatable = false),
#JoinColumn(name = "DATAKEY", insertable = false, updatable = false)
})
private CfgData cfgData;
//Getters and Setters
}
#Entity
#Table(name = "CFGDATA")
public class CfgData implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/*#EmbeddedId
private CfgDataPK id;*/
#Id
#Column(name = "FGID")
private String fgid;
#Id
#Column(name = "DATAKEY")
private String datakey;
#Column(name = "EPID", nullable = false, unique = false)
private int epid;
#Column(name = "RESERVED1", length = 45, nullable = true, unique = false)
private String reserved1;
#Column(name = "VALUE1", length = 100, nullable = true, unique = false)
private String value1;
#Column(name = "VALUE2", length = 100, nullable = true, unique = false)
private String value2;
//Getters and Setters
}
The problem I am facing is, I am not able to delete/save the entities of CfgData by passing FeatureMaster's primary id. Any operation I do is affecting only parent &child, not the grand child (CfgData) I tried a lot googling, but I cant find the solution.

How to retrieve data based on inverseColumn data using CrudRepository in springboot?

I have two tables i.e. users and events. Users table will be filled when new user will sign up. Later same user can create calendar events. so events table will be filled and users_events will keep mapping of events based on user.
I would like to find all events based on logged in userId. so here is query, it should return data based on it.
select * from events where eventid in (select eventId from users_event where id_user=x ). Here is my Users and Event Entity class.
User.java
#Entity
#Table(name = "users")
public class User {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "family_name", nullable = false)
private String familyName;
#Column(name = "e_mail", nullable = false)
private String email;
#Column(name = "phone", nullable = false)
private String phone;
#Column(name = "language", nullable = false)
private String language;
#Column(name = "id_picture")
private String pictureId;
#Column(name = "login", nullable = false)
private String login;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "enabled")
private Boolean enabled;
//getter and setter
Event.java
#Entity
#Table(name = "events")
public class Event {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "eventId", nullable = false)
private Long eventId;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "startAt", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date startAt;
#Column(name = "endAt", nullable = true)
#Temporal(TemporalType.TIMESTAMP)
private Date endAt;
#Column(name = "isFullDay", nullable = false)
private Boolean isFullDay;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "users_event", joinColumns = { #JoinColumn(name = "id_event", referencedColumnName = "eventId") }, inverseJoinColumns = { #JoinColumn(name = "id_user", table = "users", referencedColumnName = "id") })
private Set<User> user = new HashSet<User>();
/getter and setter
EventRepo.java
public interface EventRepo extends CrudRepository<Event, Long> {
Event findByUser(Set<User> user);
}
I am trying to implement something, which can give me output of this query.
select * from events where eventid in (select eventId from users_event where id_user=x )
here is my implementation.any input please?
#RequestMapping(value = "/events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents() {
logger.debug("get event list");
User x=new User();
x.setId(1);
Set<User> user= new HashSet();
user.add(x);
return (List<Event>) eventRepo.findByUser(user);
}
Just add a following method to your EventRepo:
List<Event> findAllByUserId(Long userId);
And modify your controller to something like this:
#RequestMapping(value = "/events", method = RequestMethod.GET)
public List<Event> getEvents() {
return eventRepo.findAllByUserId(1L);
}

Spring Data JPA Pageable with named entity graph

I am using Spring data with JPA 2.1 to retrieve the entities with pagination & specification (criteria builder) using below method:
Page<T> findAll(Specification<T> spec, Pageable pageable)
How can i specify #EntityGraph on this method? Rightnow, if i annotate with #EntityGraph, Spring stops doing the pagination and fetch whole data in one go.
#EqualsAndHashCode(callSuper = true)
#Data
#Entity
#Table(name = "cd_order")
#Where(clause = "deleted = " + UN_DELETED)
#NamedEntityGraphs(value = {
#NamedEntityGraph(name = "findAll", attributeNodes =
{#NamedAttributeNode(value = "company"),
#NamedAttributeNode(value = "partnerCompany"),
// remove one to many , #NamedAttributeNode(value = "orderOssUrls"),
#NamedAttributeNode(value = "patient")
})}
)
public class Order extends BaseEntity {
#Column(name = "patient_id", insertable = false, updatable = false, columnDefinition = " BIGINT NOT NULL COMMENT '订单id' ")
private Long patientId;
#OneToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT), name = "patient_id", referencedColumnName = "id")
private Patient patient;
#Column(name = "patient_name", columnDefinition = " VARCHAR(48) COMMENT '案例编号' ")
private String patientName;
#Column(name = "case_code", columnDefinition = " VARCHAR(48) NOT NULL COMMENT '案例编号' ")
private String caseCode;
#Column(name = "company_id", insertable = false, updatable = false, columnDefinition = " BIGINT COMMENT 'companyId' ")
private Long companyId;
#OneToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT), name = "company_id", referencedColumnName = "id")
#NotFound(action = NotFoundAction.IGNORE)
private Company company;
#Column(name = "partner_company_id", insertable = false, updatable = false, columnDefinition = " BIGINT COMMENT '合作伙伴的companyId' ")
private Long partnerCompanyId;
#OneToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT), name = "partner_company_id", referencedColumnName = "id")
#NotFound(action = NotFoundAction.IGNORE)
private Company partnerCompany;
#Column(name = "order_id", columnDefinition = " BIGINT NOT NULL COMMENT '订单id' ")
private Long orderId;
#Fetch(FetchMode.JOIN)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumn(foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT), name = "order_id", referencedColumnName = "order_id")
#OneToMany(fetch = FetchType.LAZY)
private List<OrderOssUrl> orderOssUrls;
#Column(name = "recovery_info", columnDefinition = " VARCHAR(128) COMMENT '恢复信息' ")
private String recoveryInfo;
/**
* 交货日期
*/
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "submit_time", columnDefinition = " datetime COMMENT '请求交货日期' ")
private LocalDateTime submitTime;
/**
* 订购日期
*/
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "order_time", columnDefinition = " datetime COMMENT '订购日期' ")
private LocalDateTime orderTime;
/**
* 扫描日期
*/
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "scan_time", columnDefinition = " datetime COMMENT '扫描日期' ")
private LocalDateTime scanTime;
#Enumerated(value = EnumType.STRING)
#Column(name = "order_type", columnDefinition = " VARCHAR(20) NOT NULL COMMENT '订单类型' ")
private OrderTypeEnum orderType;
#Enumerated(value = EnumType.STRING)
#Column(name = "cc_order_status", columnDefinition = " VARCHAR(20) COMMENT '诊所订单状态' ")
private OrderStatusEnum ccOrderStatus;
#Enumerated(value = EnumType.STRING)
#Column(name = "lab_order_status", columnDefinition = " VARCHAR(20) COMMENT '工厂订单状态' ")
private OrderStatusEnum labOrderStatus;
/**
* #param orderType 订单类型
*/
public void init(#NonNull OrderTypeEnum orderType) {
generateCaseCode();
if (orderType == OrderTypeEnum.CLINIC) {
this.setCcOrderStatus(OrderStatusEnum.NEW);
} else {
this.setLabOrderStatus(OrderStatusEnum.NEW);
}
this.setOrderType(orderType);
this.setOrderId(GlobalUniqueIdGeneratorUtils.generateOrderId());
}
/**
* 设置案例编号
*/
private void generateCaseCode() {
this.setCaseCode(getYMDStrCurrently() + "-" + this.getPatient().getName());
}
}
public interface OrderRepository extends BaseRepository<Order, Long> {
#NonNull
#EntityGraph(value = "findAll", type = EntityGraph.EntityGraphType.LOAD)
Page<Order> findAll(Specification<Order> specification, #NonNull Pageable pageable);
}
sql
select order0_.id as id1_5_0_,
patient1_.id as id1_9_1_,
company2_.id as id1_1_2_,
company3_.id as id1_1_3_,
order0_.create_time as create_t2_5_0_,
order0_.deleted as deleted3_5_0_,
order0_.update_time as update_t4_5_0_,
order0_.version as version5_5_0_,
order0_.case_code as case_cod6_5_0_,
order0_.cc_order_status as cc_order7_5_0_,
order0_.company_id as company_8_5_0_,
order0_.lab_order_status as lab_orde9_5_0_,
order0_.order_id as order_i10_5_0_,
order0_.order_time as order_t11_5_0_,
order0_.order_type as order_t12_5_0_,
order0_.partner_company_id as partner13_5_0_,
order0_.patient_id as patient14_5_0_,
order0_.patient_name as patient15_5_0_,
order0_.recovery_info as recover16_5_0_,
order0_.scan_time as scan_ti17_5_0_,
order0_.submit_time as submit_18_5_0_,
patient1_.create_time as create_t2_9_1_,
patient1_.deleted as deleted3_9_1_,
patient1_.update_time as update_t4_9_1_,
patient1_.version as version5_9_1_,
patient1_.activity_time as activity6_9_1_,
patient1_.day as day7_9_1_,
patient1_.email as email8_9_1_,
patient1_.month as month9_9_1_,
patient1_.name as name10_9_1_,
patient1_.note as note11_9_1_,
patient1_.phone as phone12_9_1_,
patient1_.sex as sex13_9_1_,
patient1_.year as year14_9_1_,
company2_.create_time as create_t2_1_2_,
company2_.deleted as deleted3_1_2_,
company2_.update_time as update_t4_1_2_,
company2_.version as version5_1_2_,
company2_.additional_info_id as addition6_1_2_,
company2_.address as address7_1_2_,
company2_.biz_type as biz_type8_1_2_,
company2_.cell_phone as cell_pho9_1_2_,
company2_.city as city10_1_2_,
company2_.country_code as country11_1_2_,
company2_.country_name as country12_1_2_,
company2_.description as descrip13_1_2_,
company2_.icon as icon14_1_2_,
company2_.language as languag15_1_2_,
company2_.mechanism_id as mechani16_1_2_,
company2_.name as name17_1_2_,
company2_.office_phone as office_18_1_2_,
company2_.postcode as postcod19_1_2_,
company2_.province as provinc20_1_2_,
company2_.time_zone as time_zo21_1_2_,
company3_.create_time as create_t2_1_3_,
company3_.deleted as deleted3_1_3_,
company3_.update_time as update_t4_1_3_,
company3_.version as version5_1_3_,
company3_.additional_info_id as addition6_1_3_,
company3_.address as address7_1_3_,
company3_.biz_type as biz_type8_1_3_,
company3_.cell_phone as cell_pho9_1_3_,
company3_.city as city10_1_3_,
company3_.country_code as country11_1_3_,
company3_.country_name as country12_1_3_,
company3_.description as descrip13_1_3_,
company3_.icon as icon14_1_3_,
company3_.language as languag15_1_3_,
company3_.mechanism_id as mechani16_1_3_,
company3_.name as name17_1_3_,
company3_.office_phone as office_18_1_3_,
company3_.postcode as postcod19_1_3_,
company3_.province as provinc20_1_3_,
company3_.time_zone as time_zo21_1_3_
from cd_order order0_
left outer join cd_patient patient1_ on order0_.patient_id = patient1_.id
left outer join cd_company company2_ on order0_.partner_company_id = company2_.id
left outer join cd_company company3_ on order0_.company_id = company3_.id
where (order0_.deleted = 0)
and order0_.order_id >= 1
and (order0_.case_code like '%case%')
and patient1_.year >= 1
and patient1_.month >= 1
and patient1_.day >= 1
and patient1_.sex = 'MALE'
and (patient1_.phone like '%phone%')
and (patient1_.name like '%name%')
and (order0_.update_time between '2021-06-23 14:40:53.101831' and '2021-06-27 14:40:53.101944')
order by order0_.create_time asc, order0_.submit_time desc
limit 10
please remove #NamedAttributeNode of on to many enter image description here,and try debug org.hibernate.hql.internal.ast.QueryTranslatorImplenter image description here
。 please care RowSelection。 i fixed the 'bug'

Resources