Trying to Convert a SQL Query to a JPA Query - spring-boot

I want to convert this sql query to a JPA query, but I can't seem make sense of it... Should I use findByMarinaIdAndMovementGroupMeanId?? or findByMarinaIdAndMovementGroupMeanIdAndMovementMeanId??
Sql:
select m.* from movement_group m
join movement_group_mean mgm on m.id = mgm.movement_group_id
join movement_mean mm on mgm.movement_mean_id = mm.id
where mm.id = 1 and m.marina_id = :marinaId and mm.active = true;
MovementGroup:
#Entity
public class MovementGroup {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String code;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Boolean active;
private String iconUrl;
#OneToMany(mappedBy = "movementGroup")
private Set<MovementGroupMean> movementGroupMeans;
#JsonIgnore
#ManyToOne()
#JoinColumn(name = "marina_id")
private Marina marina;
MovementGroupMean:
#Entity
public class MovementGroupMean {
#EmbeddedId
#JsonIgnore
private MovementGroupMeanPK movementGroupMeanPK;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "movement_group_id", insertable = false, updatable = false)
private MovementGroup movementGroup;
#ManyToOne
#JoinColumn(name = "movement_mean_id", insertable = false, updatable = false)
private MovementMean movementMean;
MovementMean:
#Entity
public class MovementMean {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#Enumerated(EnumType.STRING)
private MovementMeanType movementMeanType;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
#JsonBackReference
#ManyToOne()
#JoinColumn(name = "marina_id")
private Marina marina;

Not sure where the problem lies, so excuse the lengthy explanation on SQL->JPQL:
Replace your table names with your entity names
movement_group -> MovementGroup
Replace your joins with the java references, letting JPA use the relationship mapping you've defined instead.
"join movement_group_mean mgm on m.id = mgm.movement_group_id" becomes "join m.movementGroupMeans mgm"
"join movement_mean mm on mgm.movement_mean_id = mm.id becomes "join mgm.movementMean mm"
Only tricky spot is your entities do not define a basic mapping for the marina_id value. So to get at m.marina_id, you will have to use the 'marina' reference and use its presumably ID value:
"m.marina_id = :marinaId" -> "m.marina.id = :marinaId"
Giving you JPQL:
"Select m from MovementGroup m join m.movementGroupMeans mgm join mgm.movementMean mm where mm.id = 1 and m.marina.id = :marinaId and mm.active = true"

Related

Spring Boot Query - return list of objects with nested list of objects

I wonder if it's possible to return list of objects A where object A can have list of objects B - there is a separate table B with foreign key to the main table A. Is it possible?
#Query(value = "SELECT NEW(some dto) FROM A a " +
"JOIN B b ON a.linkToB.id = b.id " +
"WHERE a.isActive = true " +
"AND a.shipper.id = :companyId")
List<ABC> findAllActiveTempl(#Param("companyId") Long companyId);
#Entity
public class A{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "is_active")
private Boolean isActive;
#NotNull
#Column(name = "is_basic", updatable = false, insertable = false)
private Boolean isBasic;
...
}
public class B
#Column(updatable = false)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id", foreignKey = #ForeignKey(name = "FK_b"))
#ToString.Exclude
private B b;
Or maybe the only way to fetch List of B objects is a second query?
I need to receive List that contains nested List. How to do that in a most efficient way?
It is possible, just need to declare #OneToMany and #ManyToOne properly to make sure that the relationship is bidirectional.
For example, you have 2 classes, Book and Library (B and A in your case, respectively), and a relationship saying "one library can have many books". The scenario can be modelled like this:
#Entity
public class Book {
#Id
#GeneratedValue
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="library_id")
private Library library;
// other columns...
}
public class Library {
//...
#OneToMany(mappedBy = "library")
private List<Book> books;
//...
}
Make sure that you have #OneToMany declared in the Library class. The repository can be simply like this:
List<Library> findLibraryByName(String name);
After calling the findLibraryByName, you can loop through the returned list and access the books variable of each library.

Emulating a LEFT JOIN in Spring Entity

In effect, what I am trying to do is this...
SELECT a.*, b.description, c.description FROM a
LEFT JOIN b ON b.code = a.b_code
LEFT JOIN c ON c.code = a.c_code
I would like to map this entirely using Spring/JPA framework. I've played around with #SecondaryTable only to find out that it will not work in thie case, and I've been getting a number of errors while trying to map it using #OneToOne or #JoinColumn. Here are my current entity classes...
#Entity
#Table(name = "a")
public class a {
#Id
#Column(name = "id")
private String id;
#Column(name = "b_code")
private String bCode;
//I'd like to have b.description here
#Column(name = "c_code")
private String cCode;
//I'd like to have c.description here
}
#Entity
#Table(name = "b")
public class b {
#Column(name = "code")
private String code;
#Column(name = "description")
private String description;
}
#Entity
#Table(name = "c")
public class c {
#Column(name = "code")
private String code;
#Column(name = "description")
private String description;
}
You need to use #JoinColumn with referencedColumnName as shown below.
#Entity
#Table(name = "a")
public class a {
#Id
#Column(name = "id")
private String id;
#Column(name = "b_code")
private String bCode;
//I'd like to have b.description here
#Column(name = "c_code")
private String cCode;
//I'd like to have c.description here
#OneToOne // this should be based on your joining
#JoinColumn(name = "b_code", referencedColumnName = "code", insertable = false, updatable = false)
B b;
#OneToOne// this should be based on your joining
#JoinColumn(name = "c_code", referencedColumnName = "code", insertable = false, updatable = false)
C c;
}
#Entity
#Table(name = "b")
public class B {
#Column(name = "code")
private String code;
#Column(name = "description")
private String description;
}
#Entity
#Table(name = "c")
public class C {
#Column(name = "code")
private String code;
#Column(name = "description")
private String description;
}

How to write custom findAll() with Specification in JPA repository

When I use skuRepository.getAll(), it works OK, but when I apply filters, defined in Specification (List filteredRegs = skuRepository.getAll(specification)) I still get all the rows of the table
What should i do to apply the specifications to my custom method?
public interface SkuRepository extends CrudRepository<Sku, Integer>, JpaSpecificationExecutor<Sku> {
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll(#Nullable Specification<Sku> var1);
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id")
List<Sku> getAll();
}
UPD:
Here is my entities.
When I make sampling by a Sku table using the Specification API, I see three separate selects in log: one for Sku entity, one for Unit and one for Suppliers. I want my app to make one select with joins.
I read that this is due to the fact that I use EAGER fetch type, so I change it to LAZY, but then I got another problem: "InvalidDefinitionException: No serializer found..." This is logical because related entities Unit and Supplier are not loaded.
Then I decided to write my custom getAll() with request:
#Query("select s from Sku s join fetch s.unit un join fetch s.supplier sup WHERE un.id = sku_unit_id AND sup.id = supplier_id ORDER BY s.name")
But now it does not support Specification.
Please advise what to do.
#Entity
#Table(name = "sku")
public class Sku implements Cloneable, CloneableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "sku_code", length = 6, nullable = false, unique = true)
private String code;
#Column(name = "sku_name", nullable = false)
private String name;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "sku_unit_id", nullable = false)
private Unit unit;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
#Column(name = "qty_in_sec_pkg")
private int quantityInSecondaryPackaging;
#Column(name = "sku_is_active", nullable = false)
private boolean isActive;
//constructors, getters, setters
}
#Entity
#Table(name = "units")
public class Unit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "unit", nullable = false, unique = true)
private String unit;
#Column(name = "description")
private String description;
//constructors, getters, setters
}
#Entity
#Table(name = "suppliers")
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id ")
private int id;
#Column(name = "supplier_code", length = 6, nullable = false, unique = true)
private String supplierCode;
#Column(name = "supplier_name", nullable = false)
private String name;
#Column(name = "create_date", length = 19, nullable = false)
private String createDate;
#Column(name = "update_date", length = 19)
private String updateDate;
//constructors, getters, setters
}
You can't mix #Query and Specification
You can only use JpaSpecificationExecutor interface methods to use Specification.
Find more details here

springboot jpa combine two tables

I want to query data from two tables,
location field in Translation is a foreign key from id field of Location
#Entity
#Table(name = "Translation")
#Data
public class Translation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String language;
#NotNull private String name;
#NotNull private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "location", nullable = false, insertable = false, updatable = false)
#Fetch(FetchMode.JOIN)
private Location location;
}
#Entity
#Table(name = "Location")
#Data
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull private String code;
#NotNull private String type;
private Double longitude;
private Double latitude;
#Column(name = "parent_id")
private Integer parentId;
#OneToMany(targetEntity = Translation.class, mappedBy="id", fetch = FetchType.EAGER)
private Set<Translation> translations;
}
————————————————————————————————————————
But when I use a query
#Query(
"SELECT new com.afkl.travel.exercise.model.RetrieveLocationResponse("
+ "loc.code, tran.name, loc.type, loc.latitude, loc.longitude, tran.description, loc.parentId)"
+ "FROM Location loc LEFT JOIN loc.translation tran")
List<RetrieveLocationResponse> fetchLeftJoin();
All the fields related to Translation is null, having no idea what happened
UPDATE
The following ones work for me.
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;
try
#OneToMany(mappedBy = "location", cascade = CascadeType.ALL)
#JsonIgnore
private Set<Translation> translations;
#ManyToOne
#JoinColumn(name = "location")
private Location location;

QuerySyntaxException in Spring Data JPA custom query

I have a Entity:
#Entity
#Table(name = "story", schema = "")
#Data
public class Story implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sID", unique = true, nullable = false)
private Long sID;
#Column(name = "vnName", nullable = false)
private String vnName;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern = "dd-MM-yyyy HH:mm:ss")
#Column(name = "sUpdate", length = 19)
private Date sUpdate;
}
And:
#Entity
#Table(name = "chapter", schema = "")
#Data
public class Chapter implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "chID", unique = true, nullable = false)
private Long chID;
#Column(name = "chName", nullable = false)
private String chName;
#JoinColumn(name = "sID", referencedColumnName = "sID")
#ManyToOne(fetch = FetchType.LAZY)
private Story story;
}
I had created custom pojo to get the latest update story with the latest chapter:
#Data
public class NewStory{
private Story story;
private Chapter chapter;
}
but when I get list :
#Repository
public interface StoryRepository extends CrudRepository<Story, Long> {
#Query(value="SELECT NEW com.apt.truyenmvc.entity.NewStory(s as newstory, c as newchapter)"
+ " FROM story s LEFT JOIN (SELECT * FROM Chapter c INNER JOIN "
+ " (SELECT MAX(c.chID) AS chapterID FROM Story s LEFT JOIN Chapter c ON s.sID = c.sID GROUP BY s.sID) d"
+ " ON c.chID = d.chapterID) c ON s.sID = c.sID order by s.sUpdate desc")
public List<NewStory> getTopView();
}
Error:
Warning error: org.hibernate.hql.internal.ast.QuerySyntaxException: story is not mapped.
Who could help me fix it? Or could it be done in a different way?
The error is pretty self explainatory. And its just a typo in your query. You are using story. And obviously thats not mapped as an Entity.
Fix it to Story

Resources