How do I model a List of the same Entity in hibernate for a User? (Session, SessionHistory) - spring

I am making a Spring Boot Application with ORM.
I have a user, which has a current session. So modelling these two with #Entity is working great.
We have a Table Users, and Sessions. So a User is an Entity, Session is an Entity. in addition to the current session field that is mapped by a #OnetoOne. There is a SessionHistory class we'd like the user to have. And we would like it to essentially be what it's called. it has all the previous history.
Which brings me to my problem. I don't need to model that in the ERD, I think? So I can't just have it in there. Typically it would be a query that would select all session that have that user's id. How would I translate that into Java code?
#Entity
#Table(name = "users")
#Getter
#Setter
public class User {
#Setter(AccessLevel.NONE)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", nullable = false)
private Long id;
...
#Transient
private SessionHistory sessionHistory;
public User(...) {
...
// this.sessionHistory = new SessionHistory();
}
public User() {
}
}
I don't know what to try. Because I think it's not a good idea to have a table (it just doesn't make sense to me).
What I did think of, but discussion with my team said it was gonna be a problem. But I suggested Something to do with the SessionRepository, since that's our gateway to the database, but that violates the architecture if I'm not mistaken. TO have that attribute instantiated that way using either the service or repository.

The answer is to make a PostLoadEventListener and check for my entity type. That way I can make a query and set the list of sessions on User load.
Resource that helped a lot It is a little old so just be sure to research the new way of doing things

Related

JPA Inheritance strategy JOINED

I've been doing research lately on inheritance strategies of JPA.I decided to develop a new project and I decided that the most suitable strategy for me in this project is JOINED.My Entity hierarchy is like this:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Account {
#Id
#GeneratedValue
private long id;
private String iban;
}
#Entity
public class DrawingAccount extends Account{
public String drawingInfo;
}
#Entity
public class SavingsAccount extends Account{
private String savingsInfo;
}
When I create a structure in this way, the database structure is created as I want. The common field (like IBAN) of subclasses is kept on the account table.Different fields on subclasses are kept in their own tables.But when I want to fetch only common fields ( like IBAN ) from database (SELECT * FROM ACCOUNT) it is sending a JOIN query to the tables of the subclasses for me.It's nice that it does this, but I only want to see the common areas.I only want the data for the ACCOUNT table in the database. Is there a way around this? I don't want it to send a JOIN query.There is nothing wrong with sending a JOIN, but in some cases like when i need this, it should send a join query. When I don't want it to send a join query, it should not send JOIN.How can I do it?

Implementing orphanRemoval in #OneToMany

So I'm trying to remove the the child in a onetomany relationship but I'm not sure if this is the right way to do. I was reading up how to do it online but many talked about entitymanager, cacasded, using queries etc. I'm unsure on which way to do it, usually I use crudrepository and simply do .save and .deleteById etc.
Here's what I have so far
#Entity
public class User
#OneToMany(orphanRemoval = true, cascade = CascadeType.PERSIST)
private List<Payment>payment = new ArrayList<Payment>();
getters/setters
#Service
public class UserService {
public void addPayment(User user, Payment payment) {
user.getPayment().add(payment);
}
public void removePayment(User user, Payment payment) {
user.getPayment().remove(payment);
}
Do I have to mess with the cascade type or entitymanager here?
Assuming the user method parameter is an managed Entity already associated with the session, then yes, that's the correct way to do it.
When the session gets flushed, Hibernate will delete the Payment instance which was removed from the users payments.
You can try CascadeType.ALL. It work in my case.

JPA - Auto-generated field null after save

I have an Account entity and I'm trying to persist it using save function. My code:
#Override
public Account createAccount(String pin) {
Account account = new Account();
account.setBalance(0L);
account.setPin(pin);
return accountRepository.save(account);
}
Now my entity class has an autogenerated field called accountNumber. My entity class:
#Entity
#Table(name = "accounts")
#Data
public class Account {
#Column(name = "account_number", length = 32, insertable = false)
private String accountNumber;
private Long balance;
}
Now after calling save, the entity returned has accountNumber as null but i can see in the intellij database view that it is actually not null. All the other auto-generated fields like id etc are there in the returned entity just the accountNumber is null. Default value for accountNumber is set in the sql file :
ALTER TABLE accounts
ALTER COLUMN account_number SET DEFAULT DefaultValueSerializer(TRUE, TRUE, 12);
Here, DefaultValueSerializer is the function which is generating the account number.
I've tried other solutions available here like using saveAndFlush() etc, nothing worked in my case. What can be an issue?
As mentioned in comment Hibernate is not aware about what happens in database engine level so it does not see the value generated.
It would be wise to move generation of account number to JPA level instead of using db defaults.
I suggest you to study annotations #GeneratedValue and related stuff like #SequenceGenerator. That way the control of generating account number is in JPA level and there is no need for stuff like refreshing entity after save.
One starting point: Java - JPA - Generators - #SequenceGenerator
For non-id fields it is possible to generate value in method annotated with #PrePersist as other answer suggests but you could do the initialization already in the Accounts constructor .
Also see this answer for options.
You can create an annotated #PrePersist method inside the entity in which you set its fields to their default value.
That way jpa is going to be aware of the default.
There are other such annotation avaiable for different entity lifecycle event https://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html
P.s. if you decide to go this way remember to remove the insertable = false
Use
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
for your IDs. And also leave your saving to saveAndFlush so you can immediately see the changes, If any. I'd also recommend separating IDs and account numbers. They should not be the same. Try debugging your program and see where the value stops passing around.

Multiple Repositories for the Same Entity in Spring Data Rest

Is it possible to publish two different repositories for the same JPA entity with Spring Data Rest?
I gave the two repositories different paths and rel-names, but only one of the two is available as REST endpoint.
The point why I'm having two repositories is, that one of them is an excerpt, showing only the basic fields of an entity.
The terrible part is not only that you can only have 1 spring data rest repository (#RepositoryRestResource) per Entity but also that if you have a regular JPA #Repository (like CrudRepository or PagingAndSorting) it will also interact with the spring data rest one (as the key in the map is the Entity itself).
Lost quite a few hours debugging random load of one or the other. I guess that if this is a hard limitation of spring data rest at least an Exception could be thrown if the key of the map is already there when trying to override the value.
The answer seems to be: There is only one repository possible per entity.
I ended up using the #Subselect to create a second immutable entity and bound that to the second JpaRepsotory and setting it to #RestResource(exported = false), that also encourages a separation of concerns.
Employee Example
#Entity
#Table(name = "employee")
public class Employee {
#Id
Long id
String name
...
}
#RestResource
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
}
#Entity
#Immutable
#Subselect(value = 'select id, name, salary from employee')
public class VEmployeeSummary {
#Id
Long id
...
}
#RestResource(exported = false)
public interface VEmployeeRepository extends JpaRepository<VEmployeeSummary, Long> {
}
Context
Two packages in the monolithic application had different requirements. One needed to expose the entities for the UI in a PagingAndSortingRepository including CRUD functions. The other was for an aggregating backend report component without paging but with sorting.
I know I could have filtered the results from the PagingAndSorting Repository after requesting Pageable.unpaged() but I just wanted a Basic JPA repository which returned List for some filters.
So, this does not directly answer the question, but may help solve the underlying issue.
You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.
In a bit of code I wrote, I had to create two entities... one with an auto-generated id and another with a preset id, but both pointing to the same table:
#Entity
#Table("line_item")
public class LineItemWithAutoId {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
...
}
#Entity
#Table("line_item")
public class LineItemWithPredefinedId {
#Id
private String id;
...
}
Then, I had a repository for each:
public interface LineItemWithoutId extends Repository<LineItemWithAutoId,String> {
...
}
public interface LineItemWithId extends Repository<LineItemWithPredefinedId,String> {
...
}
For the posted issue, you could have two entities. One would be the full entity, with getters and setters for everything. The other, would be the entity, where there are setters for everything, but only getters for the fields you want to make public. Does this make sense?

Spring JPA one to many denormalized count field

I have two entities, Books and Comments, in a one to many relationship (one book can have many comments). I want to be able to list books and number of comments about a book. I want it denormalized, meaning the books entity will have a counter that has number of comments for that book, and it will be updated every time a comment is entered (just playing with the concept, no need to discuss about the need of denormalizing here).
I think (correct me if I am wrong) this could be easily done with a trigger in the database (whenever a new comment is created, update a counter in the books table to the corresponding bookId), but for the sake of learning I want to do it through JPA, if it makes sense.
What I have so far: //omitted some annotations, just general info
Boks entity:
#Entity
public class Books {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String author;
private Long numComments;
// getters and setters...
}
Comments entity:
#Entity
public class Comments {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String comment;
private Long authorId;
private Long bookId;
// getters and setters...
}
Books repository: I added here a query to perform the update
/**
* Spring Data JPA repository for the Books entity.
*/
public interface BooksRepository extends JpaRepository<Books,Long> {
#Modifying
#Query("UPDATE Books v SET v.numComments = v.numComments + 1 WHERE v.id = :bookId")
int updateCounter(#Param("bookId")Long bookId);
}
And now the question: What next? I think I can put the update of the Books entity annotating with #PostPersist a method of the entity Comments, but I have been unsuccessful so far. I can imagine something like this:
#PostPersist //This function in the entity Comments
protected void updateBooks() {
//Likely some call to the repository here that updates the count
// in books the info we have from current entity.
}
Any idea on how to do this? Some best practices about this kind of denormalization in JPA? Better to use the database triggers?
spring not managed your entity classes and your idea is possible but you must inject BooksRepository in enttiy class then stay at you get Nullpointerexception because spring not managed enttiy classes,The reason your BooksRepository not initlaized, try also read this post Bean injection inside a JPA #Entity and anotate entity class #Configurable after
try this
#PostPersist
protected void updateBooks(Comments comment) {
int totalComment = BooksRepository.updateCounter(comment.getBookId());
System.out.println(totalComment); // see totalComment in console
}
but good aprroach in service classes after call updateCounter when insert comment
example in your CommendService : when try a insert commend after call your updateCounter
if(comment.getBookId() != null) //Simple Control
{
CommentRepository.save(comment);
BooksRepository.updateCounter(comment.getBookId());
}

Resources