How to Fetch Custom Fields In Spring JPA - java

I want to group all similar hero names and sum the total of their 'killCount' relative to that name like:
--heroname-- --killCount--
Guson 999
Garen 934
Magnus 445
I have a Hero Entity which has these fields (but I dont intend to fetch all columns from SQL, I only need heroname and killCount)
Long heroid;
String heroname;
Integer killCount;
String heroClass;
String faction;
In my repository class I want to create a JPA query where I want to fetch rows where in I group them by 'heroname' column and the corresponding SUM/TOTAL of that hero's 'killCount'.
This blog by Baeldung suggest I create an interface. But the blog doesnt show how to actually use that in the app controller or service class. It just says create an interface.
Here's my current repository class:
public interface HeroRepository extends JpaRepository<Hero,Long>{
#Query( value = "SELECT h.heroname , SUM(h.killCount) FROM Heroes AS h GROUP BY h.heroname ORDER BY h.heroname",nativeQuery = true)
List<IHero> findAllHeroByGroupName();
}
Am I doing this right?
Service class:
public class HeroService {
#Autowired HeroRepository heroRepository;
public List<IHero> findAllHeroByGroupName() {
return heroRepository.findAllHeroByGroupName();
}
}
Interface
public interface IHero {
String getHeroName();
Integer getTotalKillCounts();
}
--UPDATE[SOLVED]--
When I do this
List<IHero> heroList = heroService.findAllHeroByGroupName();
and print via loop:
for(IHero hero: heroList){
System.out.println(hero.getHeroName);
System.out.println(hero.getTotalKillCounts);
}
hero.getHeroName contains correct value while hero.getTotalKillCounts contains NULL. Why is that? Im almost there.
ANSWER: You need to use alias equal to the field name in your Entity. So I used alias for SUM(killCount) to 'killCount' only. Bamm!
#Query( value = "SELECT h.heroname , SUM(h.killCount) as killCount FROM Heroes AS h GROUP BY h.heroname ORDER BY h.heroname",nativeQuery = true)

in the repository try to specify the parameter heroName and update the query like this by adding where clause:-
public interface HeroRepository extends JpaRepository<Hero,Long>{
#Query( value = "SELECT h.heroname , SUM(h.killCount) FROM Heroes AS h where h.heroname = ?1 GROUP BY h.heroname ORDER BY h.heroname",nativeQuery = true)
//code below will cause errors, I need to use interface says Baeldung
List<Hero> findAllHeroByGroupName(String heroName);
}

Related

Is there a way to return collection instead of entity in #Repository's #Query

I have a query:
#Query(
value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public {???} selectData()
I cannot create or return an entity for such a scheme as there is no natural id in it, so is there a way to return something like List<Triple<String, Int, LocalDateTime>>?
you can create another class with the required properties which you want to retrieve from the database and then you can return that class as List<Class>.
In your code you get the data from: scheme
So the Entity SchemeEntity should contains those three fields:
name
age
now (creationDate for example it depend on your logic)
Then your method should be like this:
#Query(value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public List<SchemeEntity> selectData();
You can provide a PROJECTION entity which will have 3 attributes and must provide a parametrized constructor with those 3 attributes you want to fetch ,or you can still get them as a List<Object[]> and then hydrate your entity.
I've tried those things but what worked for me is using ctid as an #Id. Since I only needed some sort of a mock id, it worked fine.

Using registerFunction in JPA workaround

I am trying to implement a HQL query in Jpa which uses an MySQL DATE_ADD method to increase data, as apparently Jpa doesn't seem to support native add operator for timestamp.
I read some guides which suggest a class that extends the SQLDialect, and use registerFunction to register an SQL function which can be used in a query specified by that class. However, I want to instead use this SQL function inside my Jpa class instead of separate class. Does anyone know the solution to my problem? This is my code attempt, feel me to ask me for more information.
repository class
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Conference> findByCreatorIdentifier(String creatorIdentifier);
#Query("SELECT c.id, FROM Customer c WHERE c.date > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
List<Conference> findByRecentDate(String email);
//unsuccessful SQL query
// #Query("SELECT c.id FROM Customer c WHERE DATE_ADD(c.date, INTERVAL +8 HOUR) > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
// List<Customer> findByRecentDate(String email);
}
Update
The Customer entity class stores a Date attribute as date, and is stored in UTC-time - 8h. I'm trying to get the mostRecentCustomer with reference to the current system time, which is intuitively LocalDateTime.now(), but the query doesn't seem to work too. I'm not sure if I am allowed to change the code's database schema.
registerFunction class
public class MySQLServerDialect extends MySQL5InnoDBDialect {
public MySQLServerDialect() {
registerFunction("conference.DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
// registerFunction("conference.DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
// registerFunction("addhours", new VarArgsSQLFunction(TimestampType.INSTANCE, "dateadd(minute,", ",", ")"));
}
}
You are not writing what the problem is you are facing, but I don't think that using dots in the function name is allowed. Use the following:
registerFunction("DATE_ADD", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD"));
Now the problem with MySQL is, that it requires an interval literal which requires a special keyword. There is no way to passthrough keywords from HQL to SQL directly, so you will have to use a special function e.g. DATE_ADD_HOURS:
registerFunction("DATE_ADD_HOURS", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, "DATE_ADD(?1, INTERVAL ?2 HOUR"));
and then use
#Query("SELECT c.id, FROM Customer c WHERE DATE_ADD_HOURS(c.date, 8) > current_timestamp() AND c.creatorIdentifier = ?1 ORDER BY c.date")
List<Conference> findByRecentDate(String email);

JPA Query Methods: findTopX where X is a given (variable) number?

I'm creating a RESTful API. I need to to be able to send a number to the API specifying the amount of news items it needs to return.
I already looked for some things and according to the documentation it is possible to limit your results, but I can't find anything about how to make it variable. So my question is: Is this even possible to do or do I need to write my own custom query for this.
I already tried things like:
Iterable<NewsItem> findTopAmountByOrderByDatetimeDesc(Integer amount); where Amount would be filled in by the given Integer "amount", but maybe it is stupid to even think this would be possible although it would be a nice feature in my opinion.
What I have now (not variable, so it doesn't care about the given number):
NewsItemApi:
#RequestMapping(value = "/newsitems/amount",
produces = { "application/json" },
method = RequestMethod.GET)
NewsItemRepository:
Iterable<NewsItem> findTop2ByOrderByDatetimeDesc();
NewsItemApiController:
public ResponseEntity<Iterable> getLastNewsItems(#NotNull #ApiParam(value = "amount of news items to return", required = true) #Valid #RequestParam(value = "amount", required = true) Integer amount) {
return ResponseEntity.accepted().body(this.newsItemRepository.findTop2ByOrderByDatetimeDesc());
}
You can create repository and use #Query annotation to make custom requests, something like this:
public interface NewsRepository extends JpaRepository<News, Long> {
#Query(value = "SELECT * FROM NEWS ODER BY FIELD_YOU_WANT_TO_ODER_BY DESC LIMIT ?1", nativeQuery = true)
List<News> getLatestNews(Integer amount);
}
You would have to use #Query, something like:
#Query("select n from NewsItem n order by n.datetime desc limit :num", nativeQuery = true)
Iterable<NewsItem> findTopXByOrderByDatetimeDesc(#Param("num") int num);
Of course use limit keyword according to your database.

Spring jdbcTemplate passing constant value to an query method

Is it possible to mention an values as constant in the function. I have an entity say, EntityClass. Then a repository interface over it, say
interface EntityClassRepository extends CrudRepository<EntityClass, String> {
List<EntityClass> findById(String id);
}
Can I hard code some value in the method like,
List<EntityClass> findByIdAndActiveFlagAsY(String id);
To only query the ones where active flag column's value is 'Y'.
Is it possible?
No, spring data not support AS keywords inside method names, you can check all the Supported keywords inside method names :
So to solve your problem you can use custom query instead :
#Query("from EntityClass e WHERE id = :id AND ActiveFlag = 'Y'")
public List<EntityClass> findByIdAndActiveFlagAsY(#Param("id") String id);
Or you can use Equals keywords but you should to send the value in the method :
#Query("from EntityClass e WHERE id = :id AND ActiveFlag = :flag")
public List<EntityClass> findByIdAndActiveFlagEquals(#Param("id") String id, #Param("flag") String flag);

Problems mapping Hibernate entities - native query containing left join with condition

This should be straight-forward though can't get my Hibernate entities to play nice for the following scenario with a simple two table structure:
I'm attempting to get all config names and matching config values for a given currency code (and null's where not matching).. so have written a native query to retrieve the following like so:
SELECT * FROM CONFIG_NAME LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID =
CONFIG_VALUE.CONFIG_ID AND CONFIG_VALUE.CURRENCY_CODE = '<CURRENCY_CODE>'
ORDER BY CONFIG_NAME.ID
This query doesn't seem to play nice with my Hibernate mapping as it appears to be essentially ignoring the CURRENCY_CODE clause in the join.
Essentially, for the following subset of data:
CONFIG_NAME:
CONFIG_VALUE:
There is no value defined for 'FREE_SHIPPING_ENABLED' for 'USD' so running the query above for both currency code returns as expected:
QUERY RESULTS FOR 'CAD':
QUERY RESULTS FOR 'USD':
I'm running the above query as a native query in a JpaRepository for the ConfigName entity. But what I appear to be getting is that it seems to ignore the currency_code clause in the JOIN condition. As the list of config values defined has both values for USD and CAD where they're populated. Is there an Hibernate annotation to factor this in that I'm unaware of?
It's worth bearing in mind there will only ever be ONE value defined for each config for a given currency - there's a unique constraint across CONFIG_VALUE.CONFIG_ID/CONFIG_VALUE.CURRENCY_CODE so potentially ConfigValue on the ConfigName entity would not need to be a map.
Mappings as are follows:
ConfigName - Entity
#OneToMany(mappedBy = "config")
private Set<ConfigValue> configValue;
ConfigValue - Entity
#ManyToOne(optional = false)
#JoinColumn(name="CONFIG_ID")
#Property(policy=PojomaticPolicy.NONE)
private ConfigName config;
Doesn't need to be strictly unidirectional either.. as I'm only concerned with the values from the ConfigName entity either being populated or null.
Think I'm missing something simple, so hope someone can help.
EDIT: Am querying using JpaRepository:
Am using JpaRepository to query:
#Repository
public interface ConfigNameRepository extends JpaRepository<ConfigName, Long>
{
static final String SQL_QUERY = "SELECT * FROM CONFIG_NAME "
+ "LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID = CONFIG_VALUE.CONFIG_ID "
+ "AND CONFIG_VALUE.CURRENCY_CODE = ?1 ORDER BY CONFIG_NAME.ID";
#Query(value = SQL_QUERY, nativeQuery = true)
List<ConfigName> findConfigValuesByCurrencyCode(final String currencyCode);
}
As mentioned by #Ouney, your JPA relations are not taken in account if you use a native query.
You declared a SELECT * and List<ConfigName> (the real sql result contains ConfigName+ConfigValue). So with this query, Hibernate fetchs all the ConfigName. Then, when you try to access to the set of configValue, it fetchs all the related ConfigValue.
I think this should be better/easier to use a JPQL query instead (but you need Hibernate 5.1+) :
SELECT n, v
FROM ConfigName n
LEFT JOIN ConfigValue v
ON v.config = n AND v.currencyCode = :currencyCode
ORDER BY n.id
With this method signature :
List<Object[]> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
Where the result will be :
o[0] // ConfigName
o[1] // ConfigValue (nullable)
You may want to do this prettier with a wrapper :
SELECT new my.package.MyWrapper(n, v)
...
MyWrapper constructor :
public MyWrapper(ConfigName configName, ConfigValue configValue) {
...
}
Method signature with the wrapper :
List<MyWrapper> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
(update)
I think in this case, your query can be :
SELECT n, v // or new my.package.MyWrapper(n, v)
FROM ConfigName n
LEFT JOIN n.configValue v
WITH v.currencyCode = :currencyCode
ORDER BY n.id

Categories

Resources