Unable to read from repository - spring

I have a class which tries to get a record from the db .
I see that the Query in the repository is annotated with #Transactional(readOnly = true)
When I try to do personRepository.findPersonWithName(); I get 0 records but get a record when I use findByOccupationName. Note that when I run the query on the db I do records with the given person name and When I remove the #Transactional(readOnly = true) the query returns records . Can someone pls help me understand why this is happening ?
#Component
#RequiredArgsConstructor
public class Person {
#Autowired private Ops op;
private final PersonRepository personRepository;
#SneakyThrows
public Object doTest() {
personRepository.findPersonWithName();
}
}
#Repository
public interface PersonRepository {
#Transactional(readOnly = true)
#Query(
value = "SELECT e from #{#entityName} e where e.name = ?1 )
Optional<Object> findPersonWithName(String name);
#Query("SELECT e from #{#entityName} e where e.Occupation = ?1)
Optional<DataSourceEntity> findByOccupationName(String occupationName);
}

Related

How to return just the primary key of a JPA entity [duplicate]

I am using Spring JPA to perform all database operations. However I don't know how to select specific columns from a table in Spring JPA?
For example:
SELECT projectId, projectName FROM projects
You can use projections from Spring Data JPA (doc). In your case, create interface:
interface ProjectIdAndName{
String getId();
String getName();
}
and add following method to your repository
List<ProjectIdAndName> findAll();
I don't like the syntax particularly (it looks a little bit hacky...) but this is the most elegant solution I was able to find (it uses a custom JPQL query in the JPA repository class):
#Query("select new com.foo.bar.entity.Document(d.docId, d.filename) from Document d where d.filterCol = ?1")
List<Document> findDocumentsForListing(String filterValue);
Then of course, you just have to provide a constructor for Document that accepts docId & filename as constructor args.
You can set nativeQuery = true in the #Query annotation from a Repository class like this:
public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();
Note that you will have to do the mapping yourself though. It's probably easier to just use the regular mapped lookup like this unless you really only need those two values:
public List<Project> findAll()
It's probably worth looking at the Spring data docs as well.
In my situation, I only need the json result, and this works for me:
public interface SchoolRepository extends JpaRepository<School,Integer> {
#Query("select s.id, s.name from School s")
List<Object> getSchoolIdAndName();
}
in Controller:
#Autowired
private SchoolRepository schoolRepository;
#ResponseBody
#RequestMapping("getschoolidandname.do")
public List<Object> getSchool() {
List<Object> schools = schoolRepository.getSchoolIdAndName();
return schools;
}
With the newer Spring versions One can do as follows:
If not using native query this can done as below:
public interface ProjectMini {
String getProjectId();
String getProjectName();
}
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query("SELECT p FROM Project p")
List<ProjectMini> findAllProjectsMini();
}
Using native query the same can be done as below:
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query(value = "SELECT projectId, projectName FROM project", nativeQuery = true)
List<ProjectMini> findAllProjectsMini();
}
For detail check the docs
In my case i created a separate entity class without the fields that are not required (only with the fields that are required).
Map the entity to the same table.
Now when all the columns are required i use the old entity, when only some columns are required, i use the lite entity.
e.g.
#Entity
#Table(name = "user")
Class User{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
#Column(name = "address", nullable=false)
Address address;
}
You can create something like :
#Entity
#Table(name = "user")
Class UserLite{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
}
This works when you know the columns to fetch (and this is not going to change).
won't work if you need to dynamically decide the columns.
In my opinion this is great solution:
interface PersonRepository extends Repository<Person, UUID> {
<T> Collection<T> findByLastname(String lastname, Class<T> type);
}
and using it like so
void someMethod(PersonRepository people) {
Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);
Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}
I guess the easy way may be is using QueryDSL, that comes with the Spring-Data.
Using to your question the answer can be
JPAQuery query = new JPAQuery(entityManager);
List<Tuple> result = query.from(projects).list(project.projectId, project.projectName);
for (Tuple row : result) {
System.out.println("project ID " + row.get(project.projectId));
System.out.println("project Name " + row.get(project.projectName));
}}
The entity manager can be Autowired and you always will work with object and clases without use *QL language.
As you can see in the link the last choice seems, almost for me, more elegant, that is, using DTO for store the result. Apply to your example that will be:
JPAQuery query = new JPAQuery(entityManager);
QProject project = QProject.project;
List<ProjectDTO> dtos = query.from(project).list(new QProjectDTO(project.projectId, project.projectName));
Defining ProjectDTO as:
class ProjectDTO {
private long id;
private String name;
#QueryProjection
public ProjectDTO(long projectId, String projectName){
this.id = projectId;
this.name = projectName;
}
public String getProjectId(){ ... }
public String getProjectName(){....}
}
Using Spring Data JPA there is a provision to select specific columns from database
---- In DAOImpl ----
#Override
#Transactional
public List<Employee> getAllEmployee() throws Exception {
LOGGER.info("Inside getAllEmployee");
List<Employee> empList = empRepo.getNameAndCityOnly();
return empList;
}
---- In Repo ----
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
#Query("select e.name, e.city from Employee e" )
List<Employee> getNameAndCityOnly();
}
It worked 100% in my case.
Thanks.
You can use JPQL:
TypedQuery <Object[]> query = em.createQuery(
"SELECT p.projectId, p.projectName FROM projects AS p", Object[].class);
List<Object[]> results = query.getResultList();
or you can use native sql query.
Query query = em.createNativeQuery("sql statement");
List<Object[]> results = query.getResultList();
You can apply the below code in your repository interface class.
entityname means your database table name like projects.
And List means Project is Entity class in your Projects.
#Query(value="select p from #{#entityName} p where p.id=:projectId and p.projectName=:projectName")
List<Project> findAll(#Param("projectId") int projectId, #Param("projectName") String projectName);
It is possible to specify null as field value in native sql.
#Query(value = "select p.id, p.uid, p.title, null as documentation, p.ptype " +
" from projects p " +
"where p.uid = (:uid)" +
" and p.ptype = 'P'", nativeQuery = true)
Project findInfoByUid(#Param("uid") String uid);
You can use the answer suggested by #jombie, and:
place the interface in a separate file, outside the entity class;
use native query or not (the choice depended on your needs);
don't override findAll() method for this purpose but use name of your choice;
remember to return a List parametrized with your new interface (e.g. List<SmallProject>).
Using Native Query:
Query query = entityManager.createNativeQuery("SELECT projectId, projectName FROM projects");
List result = query.getResultList();
public static final String FIND_PROJECTS = "select ac_year_id,ac_year from tbl_au_academic_year where ac_year_id=?1";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findByAcYearId(Integer ac_year_id);
this works for me
You can update your JPARepository as below.
#Query("select u.status from UserLogin u where u.userId = ?1 or u.email = ?1 or u.mobile = ?1")
public UserStatus findByUserIdOrEmailOrMobile(String loginId);
Where UserStatus is a Enum
public enum UserStatus
{
New,
Active,
Deactived,
Suspended,
Locked
}
Use:
#Query("SELECT e FROM #{#entityName} e where e.userId=:uid")
List<ClienteEnderecoEntity> findInfoByUid(#Param("uid") UUID uid);
{
"Comments":"Why not using JDBCTemplate",
"Url":"https://www.baeldung.com/spring-jdbc-jdbctemplate"
}

Spring Data JPA, parametrize #EntityGraph in CrudRepository interface

Is it possible with Spring Data JPA to do smth. like this:
public interface UserDao extends CrudRepository<User, Long> {
#EntityGraph(value = :graphName, type = EntityGraph.EntityGraphType.LOAD)
#Query(value = "SELECT DISTINCT u FROM User u")
List<User> findAllWithDetailsByGraphName(#Param(value="graphName") String graphName);
}
to be able to pass the graphName into method at run-time and invoke the load with a need set of collections? This construction is not working, producing compile time error. Any plays around it also failed...
So, I have multiple collections in User class, which I want to load on condition;
#Entity
#Table(name="user")
#NamedEntityGraphs({
#NamedEntityGraph(name = "User.details", attributeNodes = {
#NamedAttributeNode("phones"), #NamedAttributeNode("emails"), #NamedAttributeNode("pets")}),
#NamedEntityGraph(name = "User.phones", attributeNodes =
{#NamedAttributeNode("phones")}),
#NamedEntityGraph(name = "User.emails", attributeNodes =
{#NamedAttributeNode("emails")}),
#NamedEntityGraph(name = "User.pets", attributeNodes =
{#NamedAttributeNode("pets")})
})
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO, generator="native")
#GenericGenerator(name = "native", strategy = "native")
#Column(name="user_id")
private Long userId;
#Column(name="name")
private String name;
// more fields omitted
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<Phone> phones;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<Email> emails;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private Set<Pet> pets;
}
Now, I only must declare all the methods implicitly, like this:
#EntityGraph(value = "User.phones", type = EntityGraph.EntityGraphType.LOAD)
#Query(value = "SELECT DISTINCT u FROM User u")
List<User> findAllWithPhones();
Thnaks you for suggestions!
You can define a base JPA repository that accepts the entity graph as a parameter. This is particularly useful in combination with Specifications. Therefore, here follows an example based on Specifications. It's also possible to construct other queries, using different types of arguments.
#NoRepositoryBean
public interface MyBaseRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
T findOne(Specification<T> spec, EntityGraphType entityGraphType, String entityGraphName);
List<T> findAll(Specification<T> spec, Sort sort, EntityGraphType entityGraphType, String entityGraphName);
}
Implement the base repository:
#NoRepositoryBean
public class MyBaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyBaseRepository<T, ID> {
private EntityManager em;
public MyBaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.em = entityManager;
}
public MyBaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.em = em;
}
#Override
public T findOne(Specification<T> spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) {
TypedQuery<T> query = getQuery(spec, (Sort) null);
query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName));
return query.getSingleResult();
}
#Override
public List<T> findAll(Specification<T> spec, Sort sort, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) {
TypedQuery<T> query = getQuery(spec, sort);
query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName));
return query.getResultList();
}
}
Specify the custom base repository:
<jpa:repositories base-package="my.domain" base-class="my.repository.MyBaseRepositoryImpl" />
Extend from custom base repository:
public interface UserRepository extends JpaRepository<User, Long>, MyBaseRepository<User, Long>, JpaSpecificationExecutor<User> {
}
Use the custom repository's method:
Specification mySpecs = ...
List<User> user = picklistRepository.findAll(mySpecs, EntityGraphType.LOAD, "User.phones");

NamedQuery and no entity mapping

I would like to achieve the following. I have a query and I would like to run it and return rows in a REST call.
I do not want to map the query to a physical table, how would I achieve this?
I use Spring Boot 1.5.2.
After some try and fixes, I got the following solution.
Create a POJO class, no #Entity annotation. You want to add packageScan instructions if it is not found.
public class ActivityReport1 {
#Column
private BigInteger id;
#Column
private String title;
//Only getters
public ActivityReport1(BigInteger id,
String title){
this.id = id;
this.title = title;
}
In a class which is annotated with #Entity create the resultset mapping
#SqlResultSetMappings({
#SqlResultSetMapping(name = "ActivityReport1Mapping",
classes = {
#ConstructorResult(targetClass = ActivityReport1.class, columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "title")
})
})
})
Add repository class
#Repository
#Transactional
public class IActivityReport1Repository {
#PersistenceContext
private EntityManager entityManager;
public List<ActivityReport1> getResults(String userLogin) {
Query query = entityManager.createNativeQuery(
"SELECT " +
"t.request_id as id, t.request_title as title " +
"FROM some_table t ", "ActivityReport1Mapping");
List<ActivityReport1> results = query.getResultList();
return results;
}
}
And finally, the service impl class.
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class ActivityReport1ServiceImpl implements IActivityReport1Service {
private static final Logger _Logger = LoggerFactory.getLogger(ActivityReport1ServiceImpl.class);
#Autowired
private IActivityReport1Repository sessionFactory;
#Override
public List<ActivityReport1> runReport(String userLogin) {
List<ActivityReport1> reportRows = sessionFactory.getResults(userLogin);
return reportRows;
}
}
If you face with "Could not locate appropriate constructor", this means that on Java side it could not map db types to java types.
In my case I had to change id from Long to BigInteger and Timestamp to java.util.date.

Define #Query on base repository interface

Is there a way to declare a #Query on a base repsitory interface so that you don't have to declare it in all repositories? The query would have different entity names in the "FROM" part of the query.
#MappedSuperclass
public abstract class BaseAction {
#Id
Long id;
...
}
#Entity
#Table(name="AKTION_EMAIL")
public class EmailAction extends BaseAction {
private String email;
}
public interface ActionRepository<T extends BaseAction> extends JpaRepository<T, ActionPK> {
#Query("SELECT max(seqNumber) + 1 FROM ????????????? e WHERE e.order = ?1 AND e.action = ?2")
Long findNextSeqNumberByOrderAndAction(Order order, ActionConfiguration action);
}
public interface EmailActionRepository extends ActionRepository<EmailAction> {
// This works, but I don't want to repeat that in all entity repositories...
#Query("SELECT max(seqNumber) + 1 FROM EmailAction e WHERE e.order = ?1 AND e.action = ?2")
Long findNextSeqNumberByOrderAndAction(Order order, ActionConfiguration action);
}
You can use a SpEL expression in the generic query definition to refer to the unknown entity type:
interface ActionRepository<T extends BaseAction> extends JpaRepository<T, ActionPK> {
#Query("SELECT max(seqNumber) + 1 FROM #{#entityName} e WHERE …")
Long findNextSeqNumberByOrderAndAction(Order ActionConfiguration action);
}
Note how we use #{#entityName} to dynamically insert the name of the entity that re repository will be created for.

How to delete entity from database

i am new on hibernate-spring tirple..
i just try to code simple register book.. i have following codes:
Student.java
#Entity(name = "STUDENTS")
#NamedQueries({
#NamedQuery(name = "getAllStudent", query = "SELECT k FROM STUDENTS k ORDER BY k.id DESC"),
#NamedQuery(name = "findByName", query = "SELECT k FROM STUDENTS k WHERE k.name LIKE :name")
})
public class Student {
#Column(name = "STUDENTNO", nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "NAME", nullable = false)
private String name;
#Column(name = "SURNAME")
private String surname;
#Column(name = "AGE")
private String age;
// GET ve SET metods
StduentDAO.java
#Repository
#Transactional(readOnly = true)
public class StudentDAO implements IStudentDAO {
#PersistenceContext
EntityManager em;
#Override
public void deleteStudent(Student student) {
Student temp = em.getReference(Student.class, student.getId());
em.remove(temp);
System.out.println("### getting out from studentDAO deleteStudent method ###")
StudentController.java
#Component
#Scope(value = "request")
public class StudentController {
#Autowired
IStudentDAO studentDAO;
List<Student> allStudentList = new ArrayList();
Student student = new Student();
#PostConstruct
private void loadStudents() {
allStudentList = studentDAO.allStudent();
public void deleteStudent() {
studentDAO.deleteStudent(student);
System.out.println("### getting out from StudentController deleteStudent method ### ");
}
When I run deleteStudent() codes i am getting:
"### getting out from studentDAO deleteStudent method ###"
"### getting out from StudentController deleteStudent method ### "
i see these on output but nothing is deleting from database.. i searched a bit and i found this "every entitiy manager's methods open own session." that is why it says i should write my StudentDAO's deleteStudent methof like above..
i think i am missing something about transaciton but i have not recognized yet..
what should i do about this ?
Thanks..
#Transactional annotation create a transaction on your DBMS.
If you use (readOnly = true) you prevent operation on your DB (as INSERT/UPDATE/DELETE).
Remove readOnly = true so your delete method will work.

Resources