Java Spring JPA conversion to myBatis - spring

For homework I was asked to implement myBatis. However, I already started all of my project using Spring Data JPA. And so I need to translate JPA query to myBatis. here is the code in JPA:
follow this link for the code snippet
#Repository
public interface VoteRepository extends JpaRepository<Vote, Long> {
#Query("SELECT NEW com.example.polls.model.ChoiceVoteCount(v.choice.id, count(v.id)) FROM Vote v WHERE v.poll.id in :pollIds GROUP BY v.choice.id")
List<ChoiceVoteCount> countByPollIdInGroupByChoiceId(#Param("pollIds") List<Long> pollIds);
#Query("SELECT NEW com.example.polls.model.ChoiceVoteCount(v.choice.id, count(v.id)) FROM Vote v WHERE v.poll.id = :pollId GROUP BY v.choice.id")
List<ChoiceVoteCount> countByPollIdGroupByChoiceId(#Param("pollId") Long pollId);
#Query("SELECT v FROM Vote v where v.user.id = :userId and v.poll.id in :pollIds")
List<Vote> findByUserIdAndPollIdIn(#Param("userId") Long userId, #Param("pollIds") List<Long> pollIds);
#Query("SELECT v FROM Vote v where v.user.id = :userId and v.poll.id = :pollId")
Vote findByUserIdAndPollId(#Param("userId") Long userId, #Param("pollId") Long pollId);
#Query("SELECT COUNT(v.id) from Vote v where v.user.id = :userId")
long countByUserId(#Param("userId") Long userId);
#Query("SELECT v.poll.id FROM Vote v WHERE v.user.id = :userId")
Page<Long> findVotedPollIdsByUserId(#Param("userId") Long userId, Pageable pageable);
}
I'm trying to use these myBatis imports instead:
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;

Related

Spring Boot #Query annotation throwing unexpected token error on the alias

Below is the snippet of the offending code
package com.example.demo.repositories;
import com.example.demo.models.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
#Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
#Query("SELECT s FROM Student WHERE s.email = ?1")
Optional<Student> findStudentByEmail(String email);
}
This a piece of the error returned
caused by: org.hibernate.QueryException: Unable to resolve path [s.email], unexpected token [s]...
You missed alias(in your case 's') with the entity name.
so it should be,
#Query("SELECT s FROM Student s WHERE s.email = ?1")
Optional<Student> findStudentByEmail(String email);
#Query("SELECT s FROM Student s WHERE s.email = ?1")
Optional<Student> findStudentByEmail(String email);
There are two ways to do it
As already outlined in the other answers, you missed declaring the alias in the statement. Thus the correct code should be
#Query("SELECT s FROM Student s WHERE s.email = ?1")
Optional<Student> findStudentByEmail(String email);
Alternatively, if your query is simple as the one above, you can try omitting the alias as shown in the below example
#Query("FROM Student WHERE email = ?1")
Optional<Student> findStudentByEmail(String email);

How do I use custom SQL in sprint data repository?

I`m using Repository like this:
public interface UserRepository extends Repository<User, Long> {
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}
But what I need is execute my own sql statement.
select u.* from users u where exists ( select 1 from expires_users where users_id = u.id )
Please, past link references in answers.
I think you can do as pointed in the documentation of Spring Data JPA.
Here is an example:
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u.* from User u
where exists(select 1 from ExpiredUser e where e.id = u.id)")
Page<User> findExpiredUsers(Pageable pageable);
}
The Page and Pageable parts are for paging results, assuming that this query could return many more results than you would like to process at once. More information on paging results can be found here.

Data Rest Repository Custom Annotated Query

I'm working my way through the Spring Data-Rest guide and struggling writing a custom annotated query and not sure if it's even possible, here's the code:
CategoryRepository
package com.example.repositories
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.data.rest.core.annotation.RepositoryRestResource
import org.springframework.data.rest.core.annotation.RestResource
import com.example.entities.Category
import com.example.entities.InventoryDetail
#RepositoryRestResource(collectionResourceRel="categories", path="categories")
interface CategoryRepository extends JpaRepository<Category, Long> {
#RestResource(path="inventoryByCategory",rel="inventoryByCategory")
#Query("select new com.example.entities.InventoryDetail(i.id, i.item, c.name) from Category c join c.inventory i where upper(c.name) like upper(:name+'%')")
Page<InventoryDetail> queryByCategoryStartsWithIgnoreCase(#Param("name") String name, Pageable pageable)
}
The query above is the one that I'm struggling with, not sure how to properly do this. I searched for hours looking for a solution but could not find one.
Category Entity
package com.example.entities
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.OneToMany
import javax.persistence.Table
#Entity
#Table(name="categories")
class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id
#Column
String name
#Column
String description
#OneToMany(targetEntity=Inventory.class, fetch=FetchType.LAZY)
#JoinColumn(name="category")
List<Inventory> inventory
}
Inventory Entity
package com.example.entities
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.Index
import javax.persistence.Table
#Entity
#Table(name="inventory", indexes=[ #Index(columnList="category", unique=false) ])
class Inventory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id
#Column
long category
#Column
String item
#Column
String description
#Column
long price
#Column
long onHand
}
InventoryDetail
package com.example.entities
import javax.persistence.Column;
class InventoryDetail {
long id
String item
String name
InventoryDetail(long id, String item, String name) {
this.id = id
this.item = item
this.name = name
}
}
If I want to select specific fields from both entities, do I need to have a custom DTO like the one above? Is it possible to just use a new map(...) instead? Either way, the query runs and I see it in the console, but in the HAL browser it gives me a 500 error, I'm sure I am overlooking something, but not sure what it is.
I appreciate your help in advance!
EDIT
Here is the output from the Hibernate query:
Hibernate: select count(category0_.id) as col_0_0_ from categories category0_ inner join inventory inventory1_ on category0_.id=inventory1_.category where upper(category0_.name) like upper(?+'%')
Hibernate: select inventory1_.id as col_0_0_, inventory1_.item as col_1_0_, category0_.name as col_2_0_ from categories category0_ inner join inventory inventory1_ on category0_.id=inventory1_.category where upper(category0_.name) like upper(?+'%') limit ?
After countless hours of testing, I decided to throw the code in a controller and access it via the EntityManager, and it worked. After getting it to work from the controller I realized that JPA/Hibernate is expecting an Entity and not an Object/DTO.
I was able to do this...
List<Object> list(String name) {
def qry = "select new map(i.id as id, i.item as item, c.name as category) from Category c join c.inventory i where upper(c.name) like upper(:name+'%')"
List<Object> results = em.createQuery(qry).setParameter('name',name).getResultList()
return results
}
Your Query :
#Query("select new com.example.entities.InventoryDetail(i.id, i.item, c.name) from Category c join c.inventory i where upper(c.name) like upper(:name+'%')")
In your custom query no join mapping for Category and Inventory. Repace this line of code by following where Category and Inventory mapped by their id in join:
#Query("select new com.example.entities.InventoryDetail(i.id, i.item, c.name) from Category c join c.inventory i where c.id=i.category upper(c.name) like upper(:name+'%')")
Note: by default on jpql join means inner join
I'm not sure if this is the cause of your 500 error, but I see something wrong with your query, particularly with how you concatenate the paramater name with the wildcard character %.
#Query("select new com.example.entities.InventoryDetail(i.id, i.item, c.name) from Category c join c.inventory i where upper(c.name) like upper(:name+'%')")
You must remove the plus sign and the single quotes.
#Query("select new com.example.entities.InventoryDetail(i.id, i.item, c.name) from Category c join c.inventory i where upper(c.name) like upper(:name%)")
See sample implementation here.

Create a Dynamic Query in Spring

I have been using the standard JPA implementation for a while and had no problems creating dynamic queries resulting from algorithms to search for users in a database. However, we are moving to the Spring Framework (an older version - 3.2, unfortunately) and I simply cannot figure out how to create a dynamic query in Spring.
With javax.persistance.EntityManager, I could call createQuery and give it a string to work with. However, I have found that in Spring I can only use something like the following code where I define the query in an annotation.
#Repository
#SuppressWarnings("unused")
public interface PersonRepository extends JpaRepository<Person, Long>, CrudRepository<Person, Long> {
#Override
List<Person> findAll(Sort sort);
#Override
List<Person> findAll();
#Query("SELECT p FROM Person p ORDER BY p.lname ASC, p.fname ASC, p.mname ASC")
List<Person> findAllSort();
#Query("SELECT p FROM Person p WHERE UPPER(p.userName) = UPPER(?1)")
Person findPersonByUsername(String username);
}
Here is the simplest dynamic query example I could give you that I would like to replicate for Spring:
public List<Person> getPersons(List<Long> perIds) {
List<Person> persons;
String whereClause = "";
for (int i = 0; i < perIds.size(); i++) {
if (i != 0)
whereClause += " OR ";
whereClause += "p.perId = '" + perIds.get(i) + "'";
}
persons = em.createQuery("SELECT p FROM Person p WHERE " + whereClause).getResultList();
return persons;
}
Maybe a better question here is to ask if it is possible or if I should just keep my implementation using the Entity Manager. That being said, would anyone recomend me to change my code from using the EntityManager over to using the Spring Framework?
I do not know if we can do what you request.
But I have an alternative to your method.
public List<Person> getPersons(List<Long> perIds) {
return em.createQuery(
"SELECT p FROM Person p WHERE p.perId = "
+ org.springframework.util.StringUtils.collectionToDelimitedString(perIds, " OR p.perId = ", "'", "'")
).getResultList();
}
Why not use the IN query condition instead?
Spring should allow you to do:
#Query( "SELECT p FROM Person p WHERE p.perId in :ids" )
findPersonsInIdList(#Param("ids") List<Long> perIds);
Spring allows you to use #Repository, but does not force you to do so. Spring even offers a nice interfacing of JPA that separates the low level concerns (Datasource definition, and transaction management) from the high level ones (DAO, with declarative transactions).
There is a chapter in Spring Framework Reference Manual about JPA. You should also read the part about transaction management in previous chapters.
Please consider the below example.May be you are looking something like this.Though your question is not pretty straight forward what do you want.Using criteria or specification you can achieve many cool things.
#Service
Optional<Person> findByEmail(String email);
#NoRepositoryBean
public interface PersonRepositoryCustom {
Optional<Person> findByEmail(String email);
}
#Repository
public class PersonRepositoryImpl extends QuerydslRepositorySupport implements PersonRepositoryCustom {
public PersonRepositoryImpl() {
super(Person.class);
}
#Override
public Optional<Person> findByEmail(String email) {
JPAQuery<Person> query = getQuerydsl()
.createQuery()
.from(QPerson.person)
.where(QPerson.person.email.equalsIgnoreCase(email))
.select(QPerson.person);
return query.fetch().stream().findFirst();
}
}

Dynamic Queries in Spring Data JPA

I am looking for a solution to dynamically build queries using Spring Data JPA. I have a GameController which has a RESTful service endpoint /games which takes 4 optional parameters: genre, platform, year, title. The API may be passed none of those, all 4, and every combination in between. If any parameter is not passed it defaults to null. I need a method in the Repository that will build the appropriate query and ideally also still allow Spring Data JPA Paging, although I'm not sure if that is possible.
I found this article but this doesn't seem to be what I need unless I am misunderstanding. http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
I know JPA has a Query Criteria API but really have no idea how to implement this.
I realize I could create a method for each possible scenario but that seems like really bad practice and a lot of unnecessary code.
GameRepository:
package net.jkratz.igdb.repository;
import net.jkratz.igdb.model.Game;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface GameRepository extends JpaRepository<Game, Long> {
#Query("select g from Game g, GamePlatformMap gpm, Platform p where g = gpm.game and gpm.platform = p and p.id = :platform")
Page<Game> getGamesByPlatform(#Param("platform") Long platformId, Pageable pageable);
#Query("select g from Game g where g.title like :title")
Page<Game> getGamesByTitle(#Param("title") String title, Pageable pageable);
#Query("select g from Game g, GameGenreMap ggm, Genre ge where g = ggm.game and ggm.genre = ge and ge.id = :genreId")
Page<Game> getGamesByGenre(#Param("genre") Long genreId, Pageable pageable);
}
I would say that using QueryDSL is one way of doing what you want.
For example I have a repository defined as below:
public interface UserRepository extends PagingAndSortingRepository<User, Long>, QueryDslPredicateExecutor<User> {
public Page<User> findAll(Predicate predicate, Pageable p);
}
I can call this method with any combination of parameters, like below:
public class UserRepositoryTest{
#Autowired
private UserRepository userRepository;
#Test
public void testFindByGender() {
List<User> users = userRepository.findAll(QUser.user.gender.eq(Gender.M));
Assert.assertEquals(4, users.size());
users = userRepository.findAll(QUser.user.gender.eq(Gender.F));
Assert.assertEquals(2, users.size());
}
#Test
public void testFindByCity() {
List<User> users = userRepository.findAll(QUser.user.address.town.eq("Edinburgh"));
Assert.assertEquals(2, users.size());
users = userRepository.findAll(QUser.user.address.town.eq("Stirling"));
Assert.assertEquals(1, users.size());
}
#Test
public void testFindByGenderAndCity() {
List<User> users = userRepository.findAll(QUser.user.address.town.eq("Glasgow").and(QUser.user.gender.eq(Gender.M)));
Assert.assertEquals(2, users.size());
users = userRepository.findAll(QUser.user.address.town.eq("Glasgow").and(QUser.user.gender.eq(Gender.F)));
Assert.assertEquals(1, users.size());
}
}
For those using Kotlin (and Spring Data JPA), we've just open-sourced a Kotlin JPA Specification DSL library which lets you create type-safe dynamic queries for a JPA Repository.
It uses Spring Data's JpaSpecificationExecutor (i.e. JPA criteria queries), but without the need for any boilerplate or generated metamodel.
The readme has more details on how it works internally, but here's the relevant code examples for a quick intro.
import au.com.console.jpaspecificationsdsl.* // 1. Import Kotlin magic
////
// 2. Declare JPA Entities
#Entity
data class TvShow(
#Id
#GeneratedValue
val id: Int = 0,
val name: String = "",
val synopsis: String = "",
val availableOnNetflix: Boolean = false,
val releaseDate: String? = null,
#OneToMany(cascade = arrayOf(javax.persistence.CascadeType.ALL))
val starRatings: Set<StarRating> = emptySet())
#Entity
data class StarRating(
#Id
#GeneratedValue
val id: Int = 0,
val stars: Int = 0)
////
// 3. Declare JPA Repository with JpaSpecificationExecutor
#Repository
interface TvShowRepository : CrudRepository<TvShow, Int>, JpaSpecificationExecutor<TvShow>
////
// 4. Kotlin Properties are now usable to create fluent specifications
#Service
class MyService #Inject constructor(val tvShowRepo: TvShowRepository) {
fun findShowsReleasedIn2010NotOnNetflix(): List<TvShow> {
return tvShowRepo.findAll(TvShow::availableOnNetflix.isFalse() and TvShow::releaseDate.equal("2010"))
}
/* Fall back to spring API with some extra helpers for more complex join queries */
fun findShowsWithComplexQuery(): List<TvShow> {
return tvShowRepo.findAll(where { equal(it.join(TvShow::starRatings).get(StarRating::stars), 2) })
}
}
For more complex and dynamic queries it's good practice to create functions that use the DSL to make queries more readable (as you would for QueryDSL), and to allow for their composition in complex dynamic queries.
fun hasName(name: String?): Specifications<TvShow>? = name?.let {
TvShow::name.equal(it)
}
fun availableOnNetflix(available: Boolean?): Specifications<TvShow>? = available?.let {
TvShow::availableOnNetflix.equal(it)
}
fun hasKeywordIn(keywords: List<String>?): Specifications<TvShow>? = keywords?.let {
or(keywords.map { hasKeyword(it) })
}
fun hasKeyword(keyword: String?): Specifications<TvShow>? = keyword?.let {
TvShow::synopsis.like("%$keyword%")
}
These functions can be combined with and() and or() for complex nested queries:
val shows = tvShowRepo.findAll(
or(
and(
availableOnNetflix(false),
hasKeywordIn(listOf("Jimmy"))
),
and(
availableOnNetflix(true),
or(
hasKeyword("killer"),
hasKeyword("monster")
)
)
)
)
Or they can be combined with a service-layer query DTO and mapping extension function
/**
* A TV show query DTO - typically used at the service layer.
*/
data class TvShowQuery(
val name: String? = null,
val availableOnNetflix: Boolean? = null,
val keywords: List<String> = listOf()
)
/**
* A single TvShowQuery is equivalent to an AND of all supplied criteria.
* Note: any criteria that is null will be ignored (not included in the query).
*/
fun TvShowQuery.toSpecification(): Specifications<TvShow> = and(
hasName(name),
availableOnNetflix(availableOnNetflix),
hasKeywordIn(keywords)
)
for powerful dynamic queries:
val query = TvShowQuery(availableOnNetflix = false, keywords = listOf("Rick", "Jimmy"))
val shows = tvShowRepo.findAll(query.toSpecification())
JpaSpecificationExecutor supports paging, so you can achieve pageable, type-safe, dynamic queries!
I have got a solution for this. I wrote some code to extend the spring-data-jpa .
I call it spring-data-jpa-extra
spring-data-jpa-extra comes to solve three problem:
dynamic native query support like mybatis
return type can be anything
no code, just sql
You can try it : )

Resources