Spring boot, execute custom query - mysql

Im newbie to web development,and I did some examples like get data from mysql db and show them in a jsp pages.(use CRUDRepository )
but in that way we can only show only one table data.
what should we do if we want to show combine two table data.
I found these while um searching,simply I m asking how we put a more complicated sql query to this.
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.lastname like ?1%")
List<User> findByAndSort(String lastname, Sort sort);
#Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}
if we can put that complicated query (like three tables or more) here,
should we create a new entity class according to query coloumns ??
then again is that work because actually there isn't any table like that.

To get more complex data from DB you can use projections, for example:
public interface UserProjection {
Long getId();
Long getFirstNameLen();
}
#Query("select u.id as id, LENGTH(u.firstName) as firstNameLen from User u where u.lastname like ?1%")
List<UserProjection> getProjections(String lastName, Sort sort);
Note that you should use aliases in the query that must match with getters in the projection (... as firstNameLen -> getFirstNameLen())
The same way you can get data from several (joined) entities.
If you have an entity with some associations, for example:
#Entity
public class User {
//...
#OneToMany
private List<Role> roles;
}
then you can use repository method to get the users and their roles data even without any projection, just for the main entity (User). Then Spring does the rest of the work itself:
#EntityGraph(attributePaths = "roles")
List<User> findByFirstNameContainingIgnoreCase(String firstName);
or the same with query:
#Query("select distinct u from User u left join fetch u.roles where upper(p.firstName) like concat('%', upper(?1), '%')")
List<User> findWithQuery(String firstName);
In this case all users will have their lists of roles are filled with data from the roles table.
(Note to use distinct in the query to prevent the result from duplicated records, more info see here.)
Useful resources:
Spring Data JPA - Reference Documentation
Query Creation
Repository query keywords
Projections
SpEL support in Spring Data JPA #Query definitions
Hibernate ORM User Guide
Associations
HQL and JPQL
JPQL Language Reference

Related

How to execute native query with spring-data-jpa on entity with persistence relations

I'm trying to use spring-data-jpa against a mysql 5.7 database to find entities using a regular expression. My native query on a jpaRepository method is producing errors.
I'm replacing an old custom-built c++ server used for licensing with Spring. I cannot change the database structure nor the api.
I'm using spring-boot-starter-data-jpa:2.1.4.RELEASE, which users hibernate-core:5.3.9.Final and spring-data-jpa:2.1.6:RELEASE.
My api implements the following endpoint: licenses/search/{fieldName}:{regex}/{limit}/{offset}
for example: licenses/search/edition.name:"^Edition X$"/1/0
My DBLicense entity has a #OneToMany relationship with DBEdition.
At first I tried writing a query method in a LicenseRepository, as described here:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
List<DBLicense> findByEditions_NameRegex(String searchStr, Pageable pageRequest);
...
}
But I kept receiving the following error: unsupported keyword regex (1): [matchesregex, matches, regex]. The documentation indicates that regex might not be supported, and to check store-specific docs, which I could not find. Other answers led me to try the #Query annotation.
Because JPQL does not support regex, I opted to use the native query:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
#Query(value = "select l.* from licenses as l join licenseeditions as le on l.LicenseID=le.LicenseID join editions as e on le.EditionID=e.EditionID where e.Name regexp :searchStr limit :offset, :limit", nativeQuery = true)
List<DBLicense> findByEditions_NameRegex(#Param("searchStr") String searchStr, #Param("offset") Integer offset, #Param("limit") Integer limit);
...
}
and I receive the following error:
2019-07-18 11:46:50.145 WARN 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S0022
2019-07-18 11:46:50.146 ERROR 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'ParentID' not found.
My DBLicense class:
#Entity
#Table(name = "licenses")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DBLicense {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "LicenseID")
...
#ManyToOne
#JoinTable(name = "licensekinships", joinColumns = #JoinColumn(name = "ChildID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "ParentID", referencedColumnName = "LicenseID"))
private DBLicense parentLicense;
...
#OneToMany
#JoinTable(name = "licenseeditions", joinColumns = #JoinColumn(name = "LicenseID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "EditionID", referencedColumnName = "EditionID"))
#Setter(AccessLevel.NONE)
#Builder.Default
private List<DBEdition> editions = new ArrayList<DBEdition>();
}
The query executes successfully in mysql (I checked the log), and an error is thrown sometime after it returns inside Spring.
Notice that none of the tables referenced in my #Query (i.e. licenses,licenseeditions,editions) contain a 'ParentID' column. 'ParentID' is found on licensekinships, which is the relationship table of the many to one relationship between licenses and licenses.
Does my native query need to account for all the other relationship annotations on DBLicense? That's problematic because there are a LOT (the built-in LicenseRepository findById method executes no less than 59 queries!).
If you're using the hibernate/javax.persistence relationship annotations on your entities (i.e. #OneToOne, #OneToMany, #ManyToOne, #ManyToMany), and you attempt to use native queries, then you may have the same issue as presented in the question post.
This will happen especially if you have a complex schema wherein one entity shares a relationship with another, which in turn has further relationships, and you're trying to return one of those entities from a native query. To fix this error, you will need to provide enough information in your native query for spring-data-jpa to resolve the relationships which are present in your entities.
For example, consider the following class objects:
#Entity
#Table(name = "entity_a")
public class EntityA {
#Column
private int entityA_field
...
#ManyToOne
private EntityB entityB
}
and
#Entity
#Table(name = "entity_b")
public class EntityB {
#Column
private int entityB_field
...
#ManyToOne
private EntityC entityC
}
the JpaRepository built-in findById method for a EntityA id might execute multiple database queries. Using a sql database, for example:
select a.*, b.* from entity_a as a left outer join entity_b as b on a.id = b.id;
select b.*, c.* from entity_b as b left outer join entity_c as c on b.id = c.id;
you will need to mimic that first query's joins and columns. Luckily, you can see the pseudo-sql generated by spring-data-jpa by turning on logging:
find and open your application.properties (or application.yaml) file. Usually is located in "src/main/resources".
Add the following line if not present: spring.jpa.show-sql=true, then save.
Make a repository for the entity returned by your native query. For example, if your native query returns an EntityA, then your repository might look like:
#Repository
public interface MyRepository extends JpaRepository<EntityA, Long> {}
Call your repository findById method (from a controller, or a test) and check the console output. A number of queries will be logged to the console. Your native query needs to provide the same columns and implement the same joins as the first of these queries.

convert query from mysql to php laravel?

My query is the following:
select employe.id, employe.nam, users.name, users.name_user
from employe
left join users
on users.name = employe.id
it is a query to two tables: employe, users.
How can I pass it to my controller? Am I new to laravel..
I assume the user to employee is a One to One relation.
Did you setup the relation in both models?
If so you can do the following in your controller:
$employees = Employee::with('user')->all();
This will load all employees and the related user.
Question is the users.name a foreign key on the employee.id?
Thats a bit strange, i recommend using id's on both models (autoIncrement).
Laravel use a MVC pattern, a good practice is use a Model for your employe table
I recomend you to use an Eloquen Model, so, your query will look like this:
Employe::select('employe.id', 'employe.nam', 'users.name', 'users.name_user')
->leftJoin('users', 'users.name', 'employes.id')
->get();
You can easily maintain this kind of query with Eloquent relationships.
Add this method on your employee model
public function user(){
return $this->hasOne('App\User','name','id');
}
Add this method on your user model
public function Employee(){
return $this->belongsTo('App\Employee','id','name');
}
Add line on your controller
$employees = Employee::with('user')->all();

JPA: How to represent JoinTable and composite key of a JoinTable?

Let's say I have a webapp where users can follow other users.
In my database, I have a User table, and a Following table.
The Following table just has two values: a followingUserId and a followedUserId.
In Java, I have a User class. Most tutorials I see involve one object containing a set of objects it's related to. So many tutorials can describe how to have Users have a set of users following that user, and a set of users followed by a user. But that would require a lot of memory.
I'm thinking of an alternate structure where a User object has no info about following. Instead, there is a Following object that looks like this
#Entity
#Table(name = "Following")
public class Following {
RegisteredUser follower;
RegisteredUser followed;
}
and corresponds to the join table. When I want to get all the followers of a user, I can do a query for all Following objects with that user as the follower.
My issues are:
The Followers Table has a composite key of each of the two userids. How can I use annotations to represent that composite key? The #Id annotation denotes a single variable as the key
How can I do such a query?
If it's relevant, I am using MySQL as the db
If using JPA > 2.0, you can mark relationships as your ID:
#Entity
#Table(name = "Following")
#IdClass(FollowingId.class)
public class Following {
#Id
RegisteredUser follower;
#Id
RegisteredUser followed;
}
public class FollowingId implements Serializable {
private int follower;
private int followed;
}
the types within the followingId class must match the type of the RegisteredUser Id. There are plenty of more complex examples if you search on JPA derived ID.
For queries, it depends on what you are looking for. A collection of followers for a particular user could be obtained using JPQL:
"Select f.follower from Following f where f.followed = :user"

Hibernate QuerySyntaxException, Table not mapped

I'm following this Tutorial. I have added another DAO where i'm retrieving the admin_roles table. The method looks like this
public List findAllAdminRoleByUserName(String userName) {
List<AdminRoles> users = new ArrayList<AdminRoles>();
Query hqlQuery = sessionFactory.getCurrentSession().createQuery("from admin_roles AdminRoles where AdminRoles.username = ?");
users = hqlQuery.setString(0, userName).list();
if (users.size() > 0) {
return users;
} else {
return null;
}
}
When I try to retrieve i'm getting the following error
HTTP Status 500 - Request processing failed; nested exception is org.hibernate.hql.internal.ast.QuerySyntaxException: admin_roles is not mapped [from admin_roles AdminRoles where AdminRoles.username = ?]
I am able to get values from the admin table mentioned in this tutorial, also I created some other tables from which i'm able to get values. But only this table is not being mapped. I also tried by changing the name of the table from "admin_roles" to adminroles(in the database and in code) I still get the same error.
The relevant class looks like this. Also the entity annotation is javax
#Entity
#Table(name = "admin_roles", uniqueConstraints = #UniqueConstraint(columnNames = { "role", "username" }))
public class AdminRoles{
Am I missing something? Thanks in advance
You're confusing tables and entities. Tables are a relational database concept. They're mapped to entities, which are Java classes. HQL uses entities. Always. Never tables.
BTW, the message is not "Table not mapped". It's "admin_roles is not mapped". And that's very different. HQL uses entities, so it expects admin_roles in your query to be a mapped entity. Not a table name. And you don't have any entity named admin_roles.
The query should be
select ar from AdminRoles ar where ar.username = ?
That assumes there is a mapped field/property named username in the AdminRoles entity class, of course.
You need to use the entity name in you query. Try like this:
"from AdminRoles AR where AR.username = ?"

Select query in hibernate annotations in spring mvc

Hi i am writing an spring mvc, employee application using mysql database,hibernate annotations and jsp . The database contains one table "Empdata" where empid is primary key.And there is a column "team" in "Empdata".I want to select employees in a specific team, example all the details of employees in "Team1".Here i can perform delete and edit operations in the application. For delete opertaion i am using
sessionfactory.getCurrentSession().createQuery("DELETE FROM Resource WHERE empid=" +resource.getEmpId()).executeUpdate();
query.I know the commandline query for select is
SELECT * FROM EmpData ERE EMPLTEAM ="Team1"
I want to know how to convert this query into hibernate.
please help,thanks in advance..
you can convert the query in the following way:
String sql = "select ed from EmpData ed where emplTeam = :emplTeam";
Query query = session.createQuery(sql);
query.setParameter("emplTeam ", team);
List<EmpData> empDataList = (List<EmpData>)query.list();
but you should have a class called EmpData containing a property emplTeam similar to the following:
#Entity
#Table(name = "EmpData")
class EmpData {
....
#Column(name = "EMPLTEAM")
private String emplTeam;
public String getEmplTeam() {
return emplTeam;
}
public void setEmplTeam(String emplTeam) {
this.emplTeam = emplTeam;
}
}
(I used annotations hibernate .. but you can do it the same way using .hbm.xml files)
For example
Query query = session.createQuery("from Student where name=:name");
query.setParameter("name", "Raj");
In your case i guess the Entity name is Empdata(The object that represent the table)
And the field in the object is team(That has getter and setter in object)
Query query = session.createQuery("from Empdata where team=:teamParam");
query.setParameter("teamParam", "team1");