Custom sql statement using Spring JPA - spring

I am trying to insert a record into a table called user. The method that I am trying to troubleshoot is insertNewUser which is located in my class UserRepositoryCustom. I am using JPARepository and EntityManger to accomplish this. The I am still unable to insert a user into the table. Any help with this would be very much appreciated.
UserRepositoryImpl
public class UserRepositoryImpl implements UserRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public void insertNewUser(User user) {
entityManager.createNativeQuery("INSERT INTO user (username,password) VALUES (?,?)")
.setParameter(1,user.getUserName())
.setParameter(2,user.getPassword());
}
}
UserRepositoryCustom
public interface UserRepositoryCustom {
public void insertNewUser(User user);
}
UserRepository
#Repository
public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom {
User findById(int id);
User findByUserName(String userName);
}

The issue was that I was not using #Autowired on my service class's contructor to inject the repository beans. When I invoked the Repository bean methods nothing was happening. Essentially the Repository beans were just null objects.

Related

Object not initialized with autowire

I use spring boot 3
I created a object manually, FormGenerator, because everytime I use is in my advance search, some field need to be reset.
So I think the scope prototype is ok for that
#Repository
public class SchoolRepositoryCustomImpl extends SimpleJpaRepository<School, Long> implements SchoolRepositoryCustom {
#Override
public List<School> advanceSearch(SchoolSearch search) {
FormGenerator qg = new FormGenerator();
}
...
}
#Scope("prototype")
public class FormGenerator {
private int fieldCounter=0;
#Autowired
private EntityManager entityManager;
...
}
When I run application, entityManager is null?
It is null because you created the object manually by calling the constructor. You need to obtain it from the ApplicationContext. Something like this:
#Repository
public class SchoolRepositoryCustomImpl extends SimpleJpaRepository<School, Long> implements SchoolRepositoryCustom {
#Autowired
private ApplicationContext applicationContext;
#Override
public List<School> advanceSearch(SchoolSearch search) {
FormGenerator qg = applicationContext.getBean(FormGenerator.class);
}
...
}

JpaRepository mistakes custom interface as property

For general spring JpaRepository DAO (no spring-boot) , if the interface extends a custom interface , spring will mistake the interface methods as object's properties .
For example
interface ILocalDateTime {
fun getLocalDateTime() : LocalDateTime
}
interface UserDaoCustom : ILocalDateTime {
// query functions skipped
}
interface UserDao : JpaRepository<User, Long>, UserDaoCustom
class UserImpl : UserDaoCustom {
#PersistenceContext
private lateinit var em: EntityManager
override fun getLocalDateTime(): LocalDateTime {
return LocalDateTime.now()
}
// query functions skipped
}
This is a simplified UserDao. UserDaoCustom extends ILocalDateTime which contains a method getLocalDateTime .
Note : localDateTime is not a field of User.
At runtime, JpaRepository will mistake getLocalDateTime (or localDateTime ?) as User's field , and throws such exception :
Caused by: org.springframework.data.repository.query.QueryCreationException:
Could not create query for public abstract java.time.LocalDateTime foo.ILocalDateTime.getLocalDateTime()!
Reason: Failed to create query for method public abstract java.time.LocalDateTime foo.ILocalDateTime.getLocalDateTime()!
No property getLocalDateTime found for type User!;
nested exception is java.lang.IllegalArgumentException:
Failed to create query for method public abstract java.time.LocalDateTime foo.ILocalDateTime.getLocalDateTime()!
No property getLocalDateTime found for type User!
Environment :
Kotlin 1.6.20
Spring 5.3.19
spring-data-jpa 2.5.11
How to solve this problem ? (with able or unable to modify ILocalDateTime's code)
Thanks.
I think it is about naming and how Spring pick up implementations of repository extensions.
TLDR;
Change name of your implementation from UserImpl to UserDaoCustomImpl.
I have checked a similar setup and using your naming fails with the exact same error, but naming it "right" makes it work as expected
public interface ILocalDateTime {
LocalDateTime getLocalDateTime();
}
#Repository
public interface UserDao extends UserDaoCustom, JpaRepository<User, Long> {
}
public interface UserDaoCustom extends ILocalDateTime{
}
#Repository
public class UserDaoCustomImpl implements UserDaoCustom {
#Override
public LocalDateTime getLocalDateTime() {
return LocalDateTime.now();
}
}
and tests
#ExtendWith(SpringExtension.class)
#DataJpaTest
class UserRepositoryTest {
#Autowired
private UserDao userDao;
#Test
public void savesUser() {
userDao.save(new User());
}
#Test
public void bazzinga() {
assert userDao.getLocalDateTime() != null;
System.out.println("Bazzinga!"+userDao.getLocalDateTime());
}
}
yelds

Why is the #Repository annotation necesssary on a JpaRepository?

I have the following repository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
Employee findByName(String name);
}
Assume Employee is your usual entity class with id, name and surname.
I wire this class to my EmployeeService and use it like this:
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository repository;
public Employee saveEmployee(Employee employee) {
return repository.save(employee);
}
}
Would it make any difference to add the #Repository annotation to the repository?
Example:
import org.springframework.data.jpa.repository.JpaRepository;
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
Employee findByName(String name);
}
The #Repository annotation is not required on Spring Data repositories. Spring Boot detects repository beans based on the fact that they extend the Repository interface.
In fact, you need to suppress this behavior using #NoRepositoryBean if you want the repository bean to not be created.
The #Repository annotation is a specialization of #Component. If you were implementing repositories without the help of Spring Data, you could annotate them with #Repository to declare them as beans as well as hint at their role in your app.

Best practise when using Querydsl with Spring Data

Using Spring Data nad Querydsl we can just declare repository interface and skip the implementation class. Some methods with a specific name or using #Query annotation and that's all.
But sometimes I'd like to use JPAQuery and define method's body by myself, let's say
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> someMethod(String arg) {
JPAQuery query = new JPAQuery(em);
...
}
but this way I would have to implement other MyRepository interface methods, which ruins all Spring Data's advantages!
I can see two options:
Declare another interface per each repository and then normally implement it (which doubles number of interfaces)
Inject EntityManager into #Service class and implement my custom methods there
I like option #2 more, but as far I as know, in #Service class we should only call repository methods, so it's not a perfect solution as well.
So how does programmers deal with it?
You should not implement the actual Spring Data repository, instead you have to declare another custom interface where you can put your custom methods.
Let's say you have a MyRepository, defined as
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long> {}
Now you want to add your custom findTuplesByMyArg(), for a sake of purpose you need to create custom repository interface
public interface MyRepositoryCustom {
List<Tuple> findTuplesByMyArg(String myArg);
}
Afterwards comes the implementation of custom interface
public class MyRepositoryImpl implements MyRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = new JPAQuery(em);
...
}
}
And we need to change MyRepository declaration, so it extends custom repository, so that
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long>, MyRepositoryCustom {}
And you can easily access your findTuplesByMyArg() by injecting MyRepository, e.g.
#Service
public class MyService {
#Autowired
private MyRepository myRepository;
public List<Tuple> retrieveTuples(String myArg) {
return myRepository.findTuplesByMyArg(myArg);
}
}
Pay attention that names are important here (you need to have Impl postfix by default configs in repo implementation).
You can find all needed information here
I would suggest a minor rectification to the answer above, which tries to use JPAQueryFactory. It is good to make use of the provided factory class.
public class MyRepositoryImpl implements MyRepositoryCustom {
#Autowired
private JPAQueryFactory factory;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = factory.query();
...
}}
#Configuration
public class Config {
#Autowired
private EntityManager em;
#Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}

Issue with transactions in multiple services (Spring Framework/JTA): org.hibernate.ObjectDeletedException: deleted instance passed to merge

I receive the following exception during program execution:
org.hibernate.ObjectDeletedException: deleted instance passed to merge: [ns.entity.Category#<null>]; nested exception is java.lang.IllegalArgumentException: org.hibernate.ObjectDeletedException: deleted instance passed to merge: [ns.entity.Category#<null>]
The following code throws exception:
importer.foo();
Importer service:
#Service
#Transactional
public class Importer {
#Autowired
private UserService userService;
#Autowired
private CategoryService categoryService;
#Transactional
public void foo() {
User user = userService.findByLogin("max");
categoryService.delete(user.getCategories());
}
}
UserService (uses CrudRepository):
#Service
#Repository
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository repository;
#Override
#Transactional(readOnly = true)
public User findById(Long userId) {
return repository.findOne(userId);
}
}
CategoryService (uses CrudRepository):
#Service
#Repository
#Transactional
public class CategoryServiceImpl implements CategoryService {
#Autowired
private CategoryRepository repository;
#Override
#Transactional
public void delete(Set<Category> categories) {
repository.delete(categories);
}
}
The following code snippet in CategoryServiceImpl.delete() works without exception:
for (Category category : categories) {
Category newCat = findById(category.getCategoryId());
if (newCat != null) {
delete(newCat);
}
}
From what I understand two different transactions are used (one read only and one for deletion). Is it possible to re-use the transaction for all calls? Removing (readOnly = true) from UserServiceImpl.findById() does not help.
I thought that just one transaction should be used for all three methods (Importer.foo(), UserServiceImpl.findById(), CategoryServiceImpl.delete()) according to Spring documentation.

Resources