I have one simple class
#Entity
#Table(name="user")
public class User implements Serializable {
#Id
#GeneratedValue
private Integer Id;
#Length(min = 5, message = "Username must be at least 5 characters long.")
#Column(name="username",nullable=false,unique=true)
private String userName;
#ManyToMany(cascade= {CascadeType.PERSIST},fetch=FetchType.EAGER)
#JoinTable(name="user_user_profile")
private Set<UserProfile> userProfile = new HashSet<>();
}
And second class:
#Entity
#Table(name = "user_profile")
public class UserProfile {
#javax.persistence.Id
#GeneratedValue
private int Id;
#Column(name = "type", nullable = false, unique = true)
#Enumerated(EnumType.STRING)
private UserProfileType type = UserProfileType.USER;
}
public enum UserProfileType {
USER("USER"),
ADMIN("ADMIN");
}
I'm using Spring MVC and Spring Secuirty with Hibernate. Is there any way to on start of the app make every possible entry in UserProfile Entity (there is only two)? Do I have to get UserProfile from database (via TypedQuery or EntityManager.find() ) and then add it to the User to not make any exceptions?
The enum items are static in your application, so I wouldn't try to make automatic changes in the database. Adding a new record is trivial, but removing an item that is already referenced may need individual care. These values are essential for your application, so I think they should be included in your SQL scripts.
If you are using DB versioning tools such as Flyway or Liquibase, add/remove records of the user_profile table in the migration scripts. They can be configured to run the migrations before your application (and Hibernate) starts, so the application will always see the correct data.
You can add a application start up event and persist the user profiles. You can delete all the user profiles before the application shut down as well. But I wouldn't recommend this as I assume the UserProfiles wouldn't change frequently. If that is the case, you are better off preloading the user profiles via some sql script as suggested in the other answer. If you really want to do it via app, the safest way would be to delete before the app gets shut down. Following is the sample snippet. I assume you are using spring-data-jpa and provided the snippet.
#Component
public class AppStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
for(UserProfileType userProfileType: UserProfileType.values()) {
UserProfile up = new UserProfile(userProfileType);
repository.save(up);
}
}
}
#Component
public class AppStoppedListener implements ApplicationListener<ContextClosedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repository.deleteAll();
}
}
public interface UserProfileRepository extends CrudRepository<UserProfile, Integer> {
}
So I added method to dao layer:
#Transactional
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
UserProfile user=new UserProfile();
em.persist(user);
UserProfile admin=new UserProfile();
admin.setType(UserProfileType.ADMIN);
em.persist(admin);
}
And now, before adding new User i just use HQL to get persistent UserProfile object that I can add to my User. Altough it works I will probably try to load it from some sort of *.sql file since I had to add method metioned above to the Dao layer interface (because of interface type proxy) and I don't like it to be honest.
Related
I have several JPA entities, each Entity has a database user column, in that column I have to store the user that makes changes to a specific row in the table.
I created a 'MappedSuperclass' Bean that all the entities would extend from, this is the MappedSuperclass.
#MappedSuperclass
public abstract class AuditableBean {
#Column(name = "modifier_user", nullable = false)
private String modifier_user;
// Getters and Setters
}
This is one Entity that extends from the 'MappedSuperclass'.
#Entity
#Table(name = "nm_area_ref")
public class ServingAreaReferenceBean extends AuditableBean {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "nm_area_ref_id")
private UUID id;
#Column(name = "nm_srv_area_desc", nullable = false)
private String description;
#Column(name = "nm_retired", nullable = false)
private boolean retired;
// Getters and Setters
}
And, all the Beans has a corresponding service method used to save the data on the database, this is one of the services class (each service injects a repository for CRUD operations).
// Service
#Component
public class ServingAreaReferenceBO {
#Inject private ServingAreaReferenceRepository repository; //repository injection
#Inject private CloudContextProvider cloudContextProvider;
public List<ServingAreaReferenceBean> getAllServingAreaReferences() {
return Lists.newArrayList(repository.findAll());
}
public Optional<ServingAreaReferenceBean> findById(UUID id) {
return repository.findById(id);
}
public ServingAreaReferenceBean create(ServingAreaReferenceBean servingAreaReference) {
Optional<CloudContext> cloudContext = Optional.ofNullable(cloudContextProvider.get());// line 1
servingAreaReference.setUpdaterUser(cloudContext.map(CloudContext::getUserId).orElse(null));// line 2
return repository.save(servingAreaReference);// line 3
}
}
// Repository - It extends from CrudRepository (insert, update, delete operations)
#Repository
public interface ServingAreaReferenceRepository extends CrudRepository<ServingAreaReferenceBean, UUID> {
boolean existsByDescription(String description);
boolean existsByDescriptionAndIdIsNot(String description, UUID id);
}
When 'repository.save()' (line 3) executes, It stores the user successfully, but I put the user logic just before executing the save method (lines 1, 2). So I don't think that repeating those two lines on each service would be the best approach, instead, I'd like to implement a generic method or a generic class that sets the user for all the Bean Entities before executing the save method.
Is that possible? what is the better approach for that?
I was thinking to implement something like this, but not sure how to make it generic?
#Component
public class AuditableBeanHandler {
#Inject private CloudContextProvider cloudContextProvider;
public AuditableBean populateAuditInformation(AuditableBean auditableBean) {
Optional<CloudContext> cloudContext = Optional.ofNullable(CloudContextProvider.get());
auditableBean.setUpdaterUser(CloudContext.map(cloudContext::getUserId).orElse(null));
return auditableBean;
}
}
Well what I understood, you have to set user before each save call of an entities :
This can be solved by using a well known design pattern called "Template method design pattern".
Just create a parent class for service class :
public abstract class AbstractService<T extends AuditableBean> {
public AuditableBean populateAuditInformation(AuditableBean auditableBean) {
Optional<CloudContext> cloudContext = Optional.ofNullable(CloudContextProvider.get());
auditableBean.setLastUpdatedByUser(CloudContext.map(cloudContext::getUserId).orElse(null));
return auditableBean;
}
public absract T save(T areaReference);
public final T create(T t) {
t = populateAuditInformation(t);
return save(t);
}
And in your service class extends this abstract service and add save method:
public class AreaReferenceService extends AbstractService<AreaReferenceBean> {
public AreaReferenceBean save(AreaReferenceBean AreaReference) {
return repository.save(AreaReference);
}
}
While calling service method, call create() method.
hope this will solve your problem, and also you can read more about Template method design pattern.
I think you're pretty close with the AuditableBean.
Add the following configuration to enable auditing:
// Annotate a configuration class with this
#EnableJpaAuditing(auditorAwareRef = "auditAware")
Add this "auditAware" bean, you'll want to tweak it to match whatever auth mechanism you're using. It's sole purpose is to return the username of the authenticated user, Spring will use this.
#Bean
public AuditorAware<String> auditAware() {
return () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return Optional.of(authentication.getPrincipal());
};
}
Add one more annotation to your modified_user field (#LastModifiedBy). This tells Spring that when an update occurs on the entity, set this field to the value returned from your AuditAware bean.
#Column(name = "modifier_user", nullable = false)
#LastModifiedBy
private String modifier_user;
See the Spring Data JPA Documentation for more information on the available audit fields.
I've a spring boot application which uses Hibernate as an ORM and DGS framework as the graphql engine. I've been struggling with finding ways to initialize a lazy loaded collection, the proper way. I've the following scenario:
application.properties
# The below has been set to false to get rid of the anti-pattern stuff it introduces
spring.jpa.open-in-view=false
...
#Entity
public class User {
#Id
#GeneratedValue
private UUID id;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Article> articles;
...
}
#Entity
public class Article {
#Id
#GeneratedValue
private UUID id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private User user;
...
}
My User data fetcher looks something like this:
#DgsComponent
public class UserDataFetcher {
#Autowired
private UserService userService;
#DgsQuery
public User getUserById(#InputArgument UUID id) {
return userService.findById(id);
}
...
}
My UserService looks something like this:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Override
public User findById(UUID id) {
return userRepository.findById(id).orElseThrow(DgsEntityNotFoundException::new);
}
...
}
Now, I only want to initialize/load my articles collections from the DB when the user asks for it in the graphql query. For that purpose I created a child resolver for my articles which only executes when a user asks for the article in the query. My UserDataFetcher started looking like this:
#DgsComponent
public class UserDataFetcher {
#Autowired
private UserService userService;
#DgsQuery
public User getUserById(#InputArgument UUID id) {
return userService.findById(id);
}
#DgsData(parentType = "User", field = "articles")
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
Hibernate.initialize(user.getArticles());
return user.getArticles();
}
...
}
But, the above started throwing exceptions telling me that Hibernate couldn't find an open session for the above request. Which made sense because there wasn't any so I put a #Transactional on top of my child resolver and it started looking like this:
#DgsComponent
public class UserDataFetcher {
#Autowired
private UserService userService;
#DgsQuery
public User getUserById(#InputArgument UUID id) {
return userService.findById(id);
}
#DgsData(parentType = "User", field = "articles")
#Transactional
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
Hibernate.initialize(user.getArticles());
return user.getArticles();
}
...
}
However, the above didn't work either. I tried moving this #Transactional into my service layer as well but even then it didn't work and it throwed the same exception. After much deliberation, I founded out that (maybe) Hibernate.initialize(...) only works if I call it in the initial transaction, the one which fetched me my user in the first place. Meaning, it's of no use to me since my use-case is very user-driven. I ONLY want to get this when my user asks for it, and this is always going to be in some other part of my application outside of the parent transaction.
I am looking for solutions other than the following:
Changing the child resolver to something like this:
#DgsData(parentType = "User", field = "articles")
#Transactional
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
List<Article> articles = articlesRepository.getArticlesByUserId(user.getUserId);
return articles;
}
I am not in the favor of the above solution since I feel this is under-utilizing the ORM itself by trying to resolve the relation yourself rather than letting hibernate itself do it. (Correct me if I wrong thinking this way)
Changing my User entity to use FetchMode.JOIN.
#Entity
public class User {
#Id
#GeneratedValue
private UUID id;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private List<Article> articles;
...
}
This is the same as telling hibernate to eagerly load the below collection no matter what. I don't want this either.
Setting spring.jpa.open-in-view=false to spring.jpa.open-in-view=true. Not in the favor of this either since this is just a band aid for LazyInitializationExceptions.
Any other solutions that just makes your forget about LazyInitializationException by keeping the session open throughout the lifecycle of the request.
Please note this answers assumes that Spring Data JPA can be used.
Helpful can be full dynamic usage of EntityGraphs
Entity Graphs give us a possibility to define fetch plans and declare which
relations (attributes) have to be queried from the database.
According to the documentation
You can do something similar to this
productRepository.findById(1L, EntityGraphUtils.fromAttributePaths(“article, “comments”));
And pass all necessary params (relations) based on user selection to the EntityGraphUtils.fromAttributePaths method.
This give us possibility to fetch only necessary data.
Additional resources:
Sample project
Spring Blog mentioned this extension
JPA EntityGraph
EntityGraph
Another workaround I've used is to skip any child resolver and just load additional entities conditionally in the base resolver.
#DgsQuery
public User getUserById(#InputArgument UUID id) {
var user = userService.findById(id);
if (dfe.getSelectionSet().contains("articles") {
Hibernate.initialize(user.getArticles());
}
return user;
}
Is it posible to capture the entity (Book) that is being modified inside a CustomEntityTrackingListener or a CustomRevisionListener ?
Im trying to get all the information that is being passed through the apis /saveBook or /update/{id}/{pages}, not just the revision information.
When auditing an Entity in envers, it creates automatically a _AUD table for each entity and a revision table to connect the entity and its _AUD table
Using a custom revision listener I can get only the info about the revision, but I would like to reach the entity itself is being modified and saved.
...
#PostMapping("/saveBook")
public Book saveBook(#RequestBody Book book) {
return repository.save(book);
}
#PutMapping("/update/{id}/{pages}")
public Book updateBook(#PathVariable int id, #PathVariable int pages) {
Book book = repository.findById(id).get();
book.setPages(pages);
return repository.save(book);
}
...
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Data
#Audited
public class Book {
#Id
#GeneratedValue
private int id;
private String name;
private int pages;
}
#Entity
//#RevisionEntity(ExampleListener.class)
#RevisionEntity(CustomEntityTrackingRevisionListener.class)
public class ExampleRevEntity extends DefaultRevisionEntity {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#OneToMany(mappedBy="revision", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Set<ModifiedEntityTypeEntity> modifiedEntityTypes =
new HashSet<ModifiedEntityTypeEntity>();
}
public class ExampleListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
//Identity identity = (Identity) Component.getInstance("org.jboss.seam.security.identity");
exampleRevEntity.setUsername("Joaquin");
}
}
public class CustomEntityTrackingRevisionListener implements EntityTrackingRevisionListener {
#Override
public void entityChanged(Class entityClass, String entityName,
Serializable entityId, RevisionType revisionType,
Object revisionEntity) {
String type = entityClass.getName();
//((CustomTrackingRevisionEntity)revisionEntity).addModifiedEntityType(type);
((ExampleRevEntity)revisionEntity).addModifiedEntityType(type);
}
#Override
public void newRevision(Object revisionEntity) {
}
}
EntityTrackingRevisionListener.entityChanged() is executed after the object persistence, so you can get it from persistence context via find() method of your EntityManager using the identifier and entity class provided.
There are a couple ways you can accomplish this.
Introduce your own event listeners
Use a CDC (change data capture) technology like Debezium.
In the first approach, you would likely want to follow the suggestions in the Envers documentation about how you would do conditional auditing and introduce custom event listeners that extend the Envers listeners in order to deduce the changes and perform whatever tasks you need.
This can be a very daunting and tedious step because you have to understand both how Hibernate emits its data in the events, how to resolve differences, etc.
I believe the easier approach here would be to use a tool such as Debezium that enables you to setup a job that monitors a configured number of tables, in your use case the specific _AUD tables of interest. Every time Envers inserts into those tables, Debezium would react to the insert by generating an event that you can then react against asynchronously.
Debezium has several ways of being used including being embedded into an application which might be suitable for your use case or in a Kafka Connect instance that is separate from the application and provides redundancy and fault tolerance for event capture and dispatch.
I have two entities, User and Operation and both entities have a join among them:
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Basic
private String username;
private String password;
//Getters and Setters
}
#Entity
public class Operation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#ManyToOne
#JoinColumn(name = "user_id")
User user;
//Getters and Setters
}
Both Entities has a Repository too.
In my context the User entity is loadded in Session scope (HttpSession) when user (operator) has been logged.
For each operation of user on system the app register that operation throug the Operation Repository.
My question is: How can I set User entity (getting on session) to operation before the register in Database?
Is possible override the Repository method?
EDIT 1:
The operation is saved through the web interface using HTTP POST method. I need to keep using the URI to save. Like:
URI: http: // localhost: 9874 / operations
DATA: { "name": "operation-name" }
Thanks!
You can create a pre save event handler in which you can set the association: you can then make a standard Spring Data Rest post to http://localhost:9874/operations and there is no need for a custom repository or controller.
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_writing_an_annotated_handler
#RepositoryEventHandler
public class OperationEventHandler {
#HandleBeforeSave
public void handleOperationSave(Operation operation) {
}
}
You say the user is stored in the session. I take it then you are not using Spring Security? If you are then you can get the current user using a static call:
SecurityContextHolder.getContext().getAuthentication();
otherwise you would need try and wire the HttpServletRequest to your event handler or use the static wrapper call as outlined in the answers to these questions:
Spring: how do I inject an HttpServletRequest into a request-scoped bean?
From this you can get the HttpSession.
The following shows wiring in the HttpServletRequest in exactly this scenario
Spring Data Rest - How to receive Headers in #RepositoryEventHandler
so your handler looks something like:
#RepositoryEventHandler
public class OperationEventHandler {
#Autowired
private HttPServletRequest request;
#HandleBeforeSave
public void handleOperationSave(Operation operation) {
User user = (User)request.getSession().getAttribute("userKey");
operation.setUser(user);
}
}
Create a Custom Repository interface and write a implementation for that. for example.
public interface OperationRepositoryCustom {
<T extends Operation> T saveCustomized(Operation operation);
}
Your implementation class would look like this.
public class OperationRepositoryImpl implements OperationRepositoryCustom{
//You can autowire OperationRepository and other dependencies
#Autowired
OperationRepository operationRepository;
#Override
public Operation saveCustomized(Operation operation) {
//put your code to update operation with user and save it by calling operationRepository.save().
}
}
be aware of the naming convention, that Your custom implementation needs to have the same name like your repository + Impl. So if your repository interface is called OperationRepository, your Custom repository interface should be OperationRepositoryCustom and impl should be named OperationRepositoryImpl
Hope it helps
Premise:
I chose to do this because I might end up having a few thousand schemas, each having (amongst others) 1 table with a few million entries. The alternative was having (amongst others) one table with a few billion entries in one schema.
The best way to elaborate on this question is providing a simple example. Consider the following:
User.java
#Entity(name = "user")
public class User {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#Column(name = "username")
private String username;
// getters and setters...
}
UserDao.java
#Repository
public interface UserDao extends CrudRepository<User, Long> {}
UserService.java
public interface UserService {
User getUser(Long id);
}
UserServiceBean.java
#Transactional
#Service
public class UserServiceBean implements UserService {
#Autowired
private UserDao dao;
#Override
public User getUser(Long id) {
return dao.findOne(id);
}
}
UserController.java
#RestController
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(
value = "/api/users/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(
#PathVariable("id") Long id) {
User user = userService.getUser(id);
return new ResponseEntity<User>(user, HttpStatus.OK);
}
}
I would like to extend to the following functionality: supplying another ID in the URL in order to return user data from a different table.
UserController.java
...
#RequestMapping(
value = "/api/users/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(
#PathVariable("id") Long id,
#RequestParam(value = "tlbid") Long tblId) {
User user = userService.getUser(id, tblId);
return new ResponseEntity<User>(user, HttpStatus.OK);
}
Now the UserService will decode that ID into something that could be useful for spring in order to get the data from a different table.
UserServiceBean.java
...
public User getUser(Long id, Long tblId) {
Object o = doMagic(tblId);
// What should 'o' be and how could I use this?
}
All the tables have the same structure and names but different entries. The tables have to be on a different database, or in the same database but on a different schema.
I would like to know either:
a) How can I have one database connection and specify a different schema for every request.
b) How to create new database connections when necessary (I would maintain them for further requests), and specify on which connection should the request be made each time.
c) My premises are wrong and having billions of entries in a table and high concurrency does not significantly slow down query speeds.
It sounds like you're describing a multi-tenant solution. See the Hibernate documentation for a longer description and a few options for how you could partition your data.
Note: we are trying to implement the schema-based multi-tenant approach at the moment :)
In case if you are using hibernate entity class then you can always use different schemas for same datasource provided that the other schemas are accessible for the particular user mapped in Datasource.
you can use schema attribute of Table annotation in your entity class. Use the below syntax for using different schema
#Table(name="TABLE_NAME",schema="SCHEMA2")