Spring Boot REST Resource Changes not Applying - java

I have a very simple Spring Boot application, that consists of an Order and a JpaRepository #RepositoryRestResource:
#Entity
public class Order {
#Id
#GeneratedValue
private Long recordId;
#Length(max = 64)
private String orderType;
.....
}
#RepositoryRestResource
public interface OrderRepository extends JpaRepository<Order, Long>,
QuerydslPredicateExecutor<Order> {
}
I had recently changed the field orderKind to orderType. I made sure that the database had been updated. However, on the endpoints, the change isn't applying. It still expects/returns orderKind.
I ruled out any IDE issues by cleaning the project, deleting any generated sources, and running directly from the command line, to no luck.
Also verified, and QOrder.java that gets generated also had orderType defined correctly.

While writing this, discovered the problem was trivial. When I refactored the field name, I forgot to update the getter and setter for that field, such that they still read:
public String getOrderKind() {
return orderType;
}
public void setOrderKind(String orderKind) {
this.orderType = orderKind;
}
Me being new to spring boot didn't realize it was the methods that define the API, which in hindsight seems obvious, since the fields are private.

Related

Persisting nested or related objects for testing created with builder pattern and JPA/Hibernate

Let's take the following classes which are a simplification of more complex classes and their relationships.
#Data
#Builder
public class UserAccount {
private String username;
private String password;
private Language contactLanguage;
public static UserAccount.UserAccountBuilder defaultUserAccount() {
return UserAccount.builder()
.username("default_username")
.password("default_password")
.contactLanguage(defaultLanguage().build());
}
}
#Data
#Builder
public class Language {
private String name;
private String iso2;
private String iso3;
public static Language.LanguageBuilder defaultLanguage() {
return Language.builder()
.name("default_language_name")
.iso2("default_iso2")
.iso3("default_iso3");
}
}
Using Lombok's #Builder annotation, I can easily construct an object like this, especially for testing:
UserAccount.builder()
.username("foo")
.password("bar")
.contactLanguage(Language.builder()
.name("English")
.iso2("EN")
.iso3("ENG")
.build())
.build();
// Or even like this...
defaultUserAccount().build();
This works fine for unit tests or any tests where such generated objects are only required to exist in memory.
However I'd also like to use this approach for integration tests with an underlying database (using Spring Boot 2.4 + JPA + Hibernate). And this is where some issues come up I couldn't solve so far. Let's have a look:
Each UserAccount needs to have a contactLanguage, but Language lives on its own. Other entities might use it as well. When constructing a user account with defaultUserAccount().build(), then persisting this entity fails because the Language object has not been persisted yet. There is no persist cascade on contactLanguage because I don't want "any" Language being created upon creating a UserAccount.
My only idea would be to use defaultLanguage().build() and persist this before defaultUserAccount().build(). But I feel that this will become complex and flaky as soon as there are more levels of nested builders or relationship to other entites.
Another thing is: Even if I managed to persist the defaultLanguge, I would run into a collision as soon as another test calls defaultUserAccount().build() because then the langauge already exists and cannot be inserted again.
Are there any patterns or approaches for persisting such test data objects?
Update #1
After more searching, I found this question on SO which looks almost identical.

Properly extending the User Entity in Hibernate5

I'm upgrading a JHipster app written using Hibernate4 to Hibernate5.
The Hibernate4 version has been running in production just fine, but I want to upgrade to a much newer version of Spring Boot which means also Hibernate5.
I'm running into some query issues with Hibernate5 and would appreciate some extra eyes to confirm I've got the tables updated properly with correct Hibernate syntax.
// base Entity
#Entity
#Table(name = "jhi_user")
public class User extends AbstractAuditingEntity implements Serializable {
// default JHipster generated attributes
}
// additional User attributes
#Entity
public class Foo extends User {
// some extra attributes, but no "Id" field since it extends User
private String attribute1;
private String attribute2;
}
// another Entity inheriting from Foo
#Entity
public class Bar extends Foo {
private String attribute3;
private String attribute4;
}
When I'm trying to use the UserRepository to find the User account for authentication, I'm getting a lot of grief from Hibernate. I think this relationship structure is no longer valid for Hibernate5, but not sure what to do.
Update: I did locate this article - https://www.jhipster.tech/tips/022_tip_registering_user_with_additional_information.html
Update #2: The author of that article actually put a sample project together which I'm linking for anyone else finding themselves down this rabbit hole.
https://github.com/Paul-Etienne/jhipster-userextra-example - however it's missing a few key pieces (IMHO) such as updating the UserExtra.phone field. which repository do I find the User from to reference the other?
Update #3: Although I know of #MappedSuperClass, it's not an option in my specific case, as I'm working with a database that already persists the base object's data.
Update #4: If versions are a "thing" here, I'm running Spring Boot 2.2.7.RELEASE and Hibernate 5.4.15.Final
I'm experimenting down this path, but it's really causing a lot of refactoring pain, so I'll look at any other viable option.

Getting data from View using CrudRepository

For this question,I am not looking for a solution, but looking for a direction from where I can take myself ahead, hence not sharing any code.
I am preparing a REST API and I have postgresql database setup locally, which has 2 tables and one view from those 2 tables.
Normally when I want to get any data from DB, I use following code(for the sake of clarity):
DataRepository class:
public interface DataRepository extends CrudRepository<Data, String>{}
DataService class:
#Service
public class DataService {
#Autowired
private DataRepository repo;
public Data getData(String id){
return repo.findById(id).orElse(null);
}
}
DataController class:
#RestController
public class DataController{
#Autowired
private DataService service;
#RequestMapping("/{id}")
public Data getData(String id){
return service.getData(id);
}
}
Data class:
#Entity
public class Data{
#Id
private String id;
private String name;
//respective getter and setter methods
}
Now I want to retrieve data from a view, so, what should be the approach for that?
Should we use the same approach of creating Model, Service, Ctonroller and Repository classes?
Can we use CrudRepository to achieve the same?
I searched in a lot of places, but didn't find anything useful.
Let me know if anyone has any clue on this.
The reading methods of a CrudRepository should work fine with a view. For the writing methods, the view needs to be updatableenter link description here.
If you only want to read, but not to write to the repository, you can create a ReadOnlyRepository by copying the source code of the CrudRepository and removing all the writing methods.
Note that JPA will still try to persist changes made to managed entities.
To avoid that and also avoid the cost of dirty checking you can mark your entities as immutable if you are using Hibernate.

How are custom methods in spring data jpa implemented and executed?

I am using spring data jpa for my project and i have following pieces of code:
My Repository:
#Repository
#Transactional
public interface StudentRepository extends PagingAndSortingRepository<Student, Long> {
List<Student> findByIdIn(List<Long> ids);
}
and my entity is :
#Entity
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
// other fields
// getters/setters
}
Somewhere in my service class, i have
#Autowired
StudentRepository studentRepository;
and then i call findByIdIn from my service layer like :
studentRepository.findByIdIn(listOfIds);
findByIdIn(listOfIds) method works perfectly fine and everything is working as expected.
I know that the implementation of findByIdIn() method is provided by spring data jpa.
But i am not able to figure where is its implementation present?
What exactly is its implementation?
Are such methods generated at run time depending upon the method-name? If yes how are they generated and executed dynamically?
Thanks!
You can dig a little bit in the core of the Spring code to see how it works (https://github.com/spring-projects/spring-data-commons/tree/master/src/main/java/org/springframework/data/repository), but basically, it's parsing the interface methods into HQL at the startup time.
You can test just be editing a method name to a field which doesn't exist, and you'll get an error at startup time saying that there is no field as this.

how to address "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" with Spring controllers that use Converters

I'm in the middle of migrating a project from:
Spring 4.2.1 -> 5.0.0
Spring Data Gosling -> Kay
Hibernate 4.3.8 -> 5.8.0
And I'm running getting "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" when accessing an object coming from my database in a controller method.
Here's a stripped down version of my code:
// CustomUser.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "users")
public class CustomUser implements Serializable {
...
#Id
#GeneratedValue//details omitted
#GenericGenerator//details omitted
#Column(name = "id", insertable = true, updatable = true, unique = true, nullable = false)
private Long id;
#Column(name = "name")
private String name;
public String getName() { return name; }
}
// UserController.java
#RequestMapping(value = "/user/{userId}/", method = RequestMethod.GET)
public String showUser(#PathVariable("userId") CustomUser user) {
System.out.println("user name is [" + user.getName() + "]");
return "someTemplate";
}
// UserService.java
#Service
public class UserServiceImpl implements UserService {
#Autowired UserRepository userRepository;
#Override
public User findUserById(Long userId) {
return userRepository.getOne(userId);
}
}
// UserRepository.java
public interface UserRepository extends JpaRepository<CustomUser, Long> { }
// UserConverter.java
#Component
public class UserConverter implements Converter<String, CustomUser> {
#Autowired UserService userService;
#Override
public CustomUser convert(String userId) {
CustomUser user = userService.findUserById(SomeUtilClass.parseLong(userId));
return user;
}
}
There's also a #Configuration WebMvcConfigurerAdapter class that autowires a UserConverter instance and adds it to a FormatterRegistry.
Prior to starting this upgrade, I could hit:
http://server:port/user/123/
and Spring would take the "123" string, the UserConverter::convert method would fire and hit the Postgres database to look up a user with that id, and I'd get back a CustomUser object in my controller's "showUser" method.
But, now I am getting the org.hibernate.LazyInitializationException. This is occurring when I attempt to print out the user's name in the "showUser" method - or even just "println(user)" without accessing a field.
Most of the info I've been able to turn up from searching suggests that this exception comes from having an object having a lazily loaded collection of sub objects (like if my CustomUser had a collection of Permission objects or something that mapped to a different database table). But in this case I'm not even doing that, this is just a field on the object.
My best guess at the moment is this is due to some kind of hibernate session being terminated after the Converter does its work, so then back in the controller I don't have a valid session. (although again, I don't know why the CustomUser object is coming back unusable, I'm not attempting to fetch a subcollection).
I have added the Hibernate annotation "#Proxy(lazy = false)" to my CustomUser.java and if I do that the problem goes away. But, I'm not sure this is a good solution - for performance reasons, I really don't think I want to go down the road of eagerly fetching EVERYTHING.
I've also tried annotating various things (the service method, the controller method, etc.) with #Transactional; I haven't gotten that to work but I am still reasonably new to Spring and I may be trying that in the wrong place or misunderstanding what that should do.
Is there a better way to handle this than just "#Proxy(lazy = false)" on all of my Entity classes?
The immediate problem comes from the use of userRepository.getOne(userId). It is implemented in the SimpleJpaRepository using EntityManager.getReference. This method returns just a Proxy, which only contains its id. And only when a property gets accessed those get lazy loaded. This includes simple properties like name in your case.
The immediate fix is to use findOne which should load all the eager properties of your entity which should include simple properties, so the exception goes away.
Note that this will slightly change the behavior of your converter. The current version will not fail when the id is not existent in the database because it never checks. The new one will obtain an empty Optional and you'll have to convert it to null or throw an exception.
But there is (or might be) a bigger problem hiding: The entity is still detached because in absence of an explicit transaction demarcation the transactions only span single repository calls. So if you later want to access lazy loaded properties, you might get the same problem again. I see various options to consider:
Leave it as it is, being aware that you must not access lazy loaded properties.
Make sure a transaction is started before the converters get invoked.
Mark your controllers with #Transactional and loading the user (again) in the controller.

Categories

Resources