I do everything according to http://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html, but neither in-bean methods nor external ones are ever executed. What might be the cause?
#Entity
#EntityListeners(EntityListener.class)
public class User {
#Id
#Column
#GeneratedValue
private Integer id;
// etc...
#PostConstruct
#PostLoad
#PostPersist
#PostUpdate
public void magic() {
System.out.println("YES I AM EXECUTED!");
System.exit(123);
}
}
OR
#Entity
#EntityListeners(MyListener.class)
public class User {
#Id
#Column
#GeneratedValue
private Integer id;
// etc...
}
+
public class MyListener {
#PostPersist
void postPersist(Object object) {
System.out.println("CAN'T BELEIVE I SEE THIS!");
System.exit(234);
}
}
My code creates, saves and loads beans, but nothing happens on the listeners. This is a piece of the repository thats perform the operations:
#Repository
public class UserRepositoryImpl implements UserRepository {
#Autowired
private SessionFactory sessionFactory;
#Override
public User get(Integer id) {
return sessionFactory.getCurrentSession().get(User.class, id);
}
#Override
public User save(User user) {
Session session = sessionFactory.getCurrentSession();
user = (User) session.merge(user);
session.saveOrUpdate(user);
return user;
}
// etc...
}
Repository methods are called from services like this one:
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Override
public void something() {
// just some repo calls + extra logic
}
}
I do not think I got something special here.
JPA interceptors mechanism work only when you manipulate entities via JPA EntityManager, they have no effect when you're using Hibernate Session directly.
You'll have to implement Hibernate native interceptors if you want to use the Session API.
Related
For example I have User entity class.
This User have a status field.
When status is changed I need to call method from my UserService.
I have this code, but I think injecting service into entity is very bad.
#Entity
public class User {
private String status;
#Transient
private String lastStatus;
#Transient
#Autowired
private UserService userService;
public String getStatus() {
return status;
}
public void setStatus(String status) {
lastStatus = this.status;
this.status = status;
}
#PreUpdate
public void handleChangeStatus() {
if (!status.equals(lastStatus)) {
userService.doSomething();
}
}
}
One way to do this is to publish domain events from your User entity class and then to create a listener for this events.
First, let's create an event:
#AllArgsConstructor
#Getter
public class UserStatusChangeEvent {
private final UUID userId;
private final String lastStatus;
private final String newStatus;
}
Then your User entity class should implement AbstractAggregateRoot interface with default domain event publishing mechanism to let your entity publish events and publish one in your setStatus method:
#Entity
#Getter
public class User implements AbstractAggregateRoot<User> {
private UUID userId;
private String status;
#Transient
private String lastStatus;
public void setStatus(String status) {
lastStatus = this.status;
this.status = status;
registerEvent(new UserStatusChangeEvent(userId, lastStatus, status));
}
}
Then create a separate class with a listener, define it as a bean (#Component) and inject there your UserService:
#RequiredArgsConstructor
#Component
public class UserStatusListener {
private final UserService userService;
#EventListener
public void onStatusChange(UserStatusChangeEvent statusChangeEvent) {
if (!statusChangeEvent.getNewStatus().equals(statusChangeEvent.getLastStatus())) {
userService.doSomething();
}
}
}
Spring will do the "magic" for you - publish your event as a application event and register your listener on startup.
Note, that spring-data will publish your domain events only after save or saveAll method called on a repository of your entity, so no save - no events.
Also instead of #EventListener you can use #TransactionalEventListener with different transaction phase if you want your listener to work depending on the transaction (success, failure, before or after, etc.).
P.S. I used Lombok annotations on classes to simplify code and inject fields through the constructor.
I have a SDR project and I am successfully validating the user entity for POST request but as soon as I update an existing entity using either PATCH or PUT the DB is updated BEFORE the validation is executed (the validator is being executed and error is returned but the DB is being updated anyway).
Do I need to setup a separate config for update ? Am I missing an extra step for that?
Entity
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Member {
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_id_gen")
#SequenceGenerator(name = "member_id_gen", sequenceName = "member_id_seq")
#Id
#JsonIgnore
private long id;
#Version
private Integer version;
#NotNull
protected String firstName;
#NotNull
protected String lastName;
#Valid
protected String email;
}
Repository
#RepositoryRestResource(collectionResourceRel = "members", path = "member")
public interface MemberRepository extends PagingAndSortingRepository<Member, Long> {
public Member findByFirstName(String firstName);
public Member findByLastName(String lastName);
}
Validator
#Component
public class BeforeSaveMemberValidator implements Validator {
public BeforeSaveMemberValidator() {}
private String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$";
#Override
public boolean supports(Class<?> clazz) {
return Member.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Member member = (Member) target;
if(ObjectUtils.isEmpty(member.getFirstName())) {
errors.rejectValue("firstName", "member.firstName.empty");
}
if(ObjectUtils.isEmpty(member.getLastName())) {
errors.rejectValue("lastName", "member.lastName.empty");
}
if(!ObjectUtils.isEmpty(member.getDni()) && !member.getDni().matches("^[a-zA-Z0-9]*$")) {
errors.rejectValue("dni", "member.dni.invalid");
}
if(!ObjectUtils.isEmpty(member.getEmail()) && !member.getEmail().matches(EMAIL_REGEX)) {
errors.rejectValue("email", "member.email.notValid");
}
}
}
BeforeSave service
#Service
#RepositoryEventHandler(Member.class)
public class MemberService {
#HandleBeforeCreate
#HandleBeforeSave
#Transactional
public void beforeCreate(Member member) {
...
}
}
I think you should rename your validator, for example, to MemberValidator then assign it as described here:
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new MemberValidator());
v.addValidator("beforeSave", new MemberValidator());
}
But I suggest you to use Bean validation instead of your custom validators. To use it in SDR project you can inject LocalValidatorFactoryBean, then assign it for 'beforeCreate' and 'beforeSave' events in configureValidatingRepositoryEventListener:
#Configuration
#RequiredArgsConstructor // Lombok annotation
public class RepoRestConfig extends RepositoryRestConfigurerAdapter {
#NonNull private final LocalValidatorFactoryBean validatorFactoryBean;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", validatorFactoryBean);
v.addValidator("beforeSave", validatorFactoryBean);
super.configureValidatingRepositoryEventListener(v);
}
}
In this case your SDR will automatically validate payloads of POST, PUT and PATCH requests for all exposed SDR repositories.
See my example for more details.
How should I manage the create, edit and delete operations with entites as simple as possible?
For example:
User:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
...
// Item can't exist without user
#OneToMany(cascade=CascadeType.ALL,mappedBy = "user",orphanRemoval=true)
private Set<Item> items = new HashSet<Item>();
public Set<Item> getItems() { return items; }
public void addItem(Item item) {
items.add(item);
}
public void removeItem(Item item) {
if(!items.contains(item)) return;
items.remove(item);
}
// Group can exist without a user
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH},mappedBy="users")
private Set<Group> groups = new HashSet<Group>();
public Set<Group> getGroups() { return groups; }
public void setGroups(Set<Group> groups) { this.groups = groups; }
public void addGroup(Group group) {
groups.add(group);
}
publi void removeGroup(Group group) {
if(!groups.contains(group)) return;
groups.remove(group);
}
...
}
Group:
#Entity
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
...
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinTable(name = "GroupToUser", joinColumns =
#JoinColumn(name = "GroupId", referencedColumnName="Id"), inverseJoinColumns =
#JoinColumn(name = "UserId", referencedColumnName="Id"))
private Set<User> users = new HashSet<User>();
public Set<User> getUsers() { return users; }
public void setUsers(Set<User> users) { this.users = users; }
public void addUser(User user) {
user.addGroup(this);
}
publi void removeUser(User user) {
if(!users.contains(user)) return;
users.remove(user);
}
...
}
Item:
#Entity
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
...
#ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="UserId")
private User user;
public Set<User> getUser() { return user; }
public void setUser(User user) {
this.user = user;
}
publi void removeUser() {
this.user = null;
}
...
}
Am I using the jpa annotations right?
What should I write here?
EntityManager em = getEntityManager();
em.getTransaction().begin();
???
em.getTransaction().commit();
Do I have to just call the em.remove/persist/merge methods for delete/create/edit operations?
And when should I use the javax.persistence.EntityManager.getReference method in these operations?
Find() delivers the entity from the cache of the persistence context or if he is not there, it will be loaded from the database.
GetReference() does not load the entity immediately. A proxy( a certain object, a so called "deputy" with enriched methods for loading the actual entity) is returned. So it is a realisation with help of LazyLoading.
Only if the attributes of the proxy or other persistence methods are needed/called the proxy interacts and loads the actual entity from the database.
Eg:
User us = em.find(User.class, 70992);
GetReference() is used similarly.
User us = em.getReference(User.class, 70922);
If the entity for the user id is not known in the persistence context, an EntityNotFoundException() is thrown.
I usually use getReference method when i do not need to access database state (I mean getter method). Just to change state (I mean setter method).
In above case if I want to update age of user like below after getting user:
setAge(age);
If i call find method, JPA provider, behind the scenes, will call
SELECT NAME, AGE FROM USER WHERE USER_ID = ?
UPDATE USER SET AGE = ? WHERE USER_ID = ?
If i call getReference method, JPA provider, behind the scenes, will call
UPDATE PERSON SET AGE = ? WHERE USER_ID = ?
Because when you call getReference, you will get a proxy object.
For rest we have to use remove, persist and merge like you have said
Personally I recommend reading about the Repository Software Pattern and the Single Responsability Principle.
My idea would be to create, for instance, a UserRepository and another class, like a Controller, would go to that repository after creating the necessary objects and try to persist them.
Should work like this:
First get the entity manager and the transaction as you did, then try to persist() the entities. If the persist() method detects that the entity is in the persistence, it will throw a PersistenceException. Catch it, obtain a new transaction and call the merge() method.
The easiest way is not the best way. The best easiest way is this one, I think (but concerning update actions, you better read more about JPQL namedQueries / orm.xml):
#WebFilter("*.htm")
public class JPAFilter implements Filter {
private static final EntityManagerFactory entityManagerFactory
= Persistence.createEntityManagerFactory(/* yourprojectname */);
private static final ThreadLocal<EntityManager> entityManagers
= new ThreadLocal<>();
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
entityManagers.set(entityManagerFactory.createEntityManager());
EntityManager entityManager = entityManagers.get();
try {
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
} finally {
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
entityManager.close();
entityManagers.remove();
}
}
public static EntityManager getEntityManager() {
return entityManagers.get();
}
#Override
public void destroy() {
entityManagerFactory.close();
}
}
----
abstract class AbstractDAO {
protected EntityManager getEntityManager() {
return JPAFilter.getEntityManager()
}
}
----
public class UserDAO extends AbstractDAO {
public void create(User user) {
getEntityManager().persist(user);
}
public User read(long id) {
return getEntityManager().find(User.class, id);
}
public void delete(long id) {
if (user != null) {
getEntityManager().remove(user);
}
}
}
----
abstract class AbstractService {
private EntityManager getEntityManager() {
return JPAFilter.getEntityManager();
}
protected void beginTransaction() {
getEntityManager().getTransaction().begin();
}
protected void commit() {
getEntityManager().getTransaction().commit();
}
protected void rollback() {
getEntityManager().getTransaction().rollback();
}
}
----
public class UserService extends AbstractService {
private final UserDAO userDAO = new UserDAO();
public void create(User user) {
beginTransaction();
userDAO.create(user);
commit();
}
public User read(long id) {
return userDAO.read(id)
}
public void delete(long id) {
userDAO.delete(id);
}
}
----
#WebServlet("/users.htm")
public class ManageUsersServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String VIEW = "/WEB-INF/JSP/users.jsp";
private final transient UserService userService = new UserService();
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// here you do whatever you want using your userService methods.
}
}
P.S. Do not forget to lock records from database when updating (using pessimistic lock or the catching an optimistic lock exception).
You can also find lots about it if you search for "CRUD Operations with JPA". First tutorial I found is this one: http://www.objectdb.com/java/jpa/persistence/crud
Am I using the jpa annotations right?
At first glance, I'd only question the absence of a #Version field, which is necessary for the EntityManager to perform optimistic locking, which is the standard safeguard against lost updates unless you use a serializable isolation level (which would require special configuration on many database systems).
Do I have to just call the em.remove/persist/merge methods for delete/create/edit operations?
That is the easiest and usually best way.
And when should I use the javax.persistence.EntityManager.getReference method in these operations?
getReference is useful if you want to refer to an entity without loading its data from the database. For instance, you could have:
public void updateUser(Item item, int userId) {
item.setUser(entityManager.getReference(User.class, userId)); // does not require a query
entityManager.merge(item);
}
Transactions
You'll want to rollback the transaction if an exception is thrown and close the entityManager once you are done with it (both in case of success and failure).
On a related note, if your application is not tiny, you'll want to look into declarative transaction management so you don't have to code them for every operation. For instance, in Spring you'd simply annotate the class with #Transactional to have Spring start (and end) a transaction whenever a method is invoked.
I want to log changes of an account. Therefore, I have created an entity class that shall log the changes.
Each time, an account entity is saved or updated a logging object is created.
When the object is updated with a new balance the old balance shall be retrieved from the database.
As the object is bound to the session retrieving its old balance is not trivial, because one always gets the new balance.
To circumvent, I detached the object from the session. Yet, this seems to be a workaround that should be avoided.
The following code snippets shall illustrate the scenario.
Any suggestion is highly appreciated!
The test:
public class AccountServiceTest
{
#Autowired
AccountService accountService;
#Autowired
ChangeAccountService changeAccountService;
#Test
public void shouldHaveChangeLog()
{
Account account = this.accountService.updateAccount(new Account(0, 10.0));
assertThat(account.getId(), is(not(0L)));
account.setBalance(20.0);
account = this.accountService.updateAccount(account);
final List<ChangeAccountLog> changeResultLogs = this.changeAccountService.findAll();
assertThat(changeResultLogs.get(1).getNewBalance(), is(not(changeResultLogs.get(1).getOldBalance())));
}
}
The service of the domain class to be logged:
#Service
public class AccountService
{
#Autowired
AccountRepository accountRepository;
#Autowired
ChangeAccountService changeAccountService;
public Account findById(final long id)
{
return this.accountRepository.findOne(id);
}
public Account updateAccount(final Account account)
{
this.changeAccountService.saveLog(account);
return this.accountRepository.save(account);
}
}
The service of the logging class:
#Service
public class ChangeAccountService
{
#Autowired
AccountService accountService;
#Autowired
ChangeAccountLogRepository repository;
public ChangeAccountLog save(final ChangeAccountLog changeAccountLog)
{
return this.repository.save(changeAccountLog);
}
public List<ChangeAccountLog> findAll()
{
return this.repository.findAll();
}
public ChangeAccountLog saveLog(final Account account)
{
final Double oldAccountBalance = oldAccountBalance(account);
final Double newAccountBalance = account.getBalance();
final ChangeAccountLog changeAccountLog = new ChangeAccountLog(0, oldAccountBalance, newAccountBalance);
return this.repository.save(changeAccountLog);
}
#PersistenceContext
EntityManager em;
private Double oldAccountBalance(final Account account)
{
this.em.detach(account);
final Account existingAccount = this.accountService.findById(account.getId());
if (existingAccount != null)
{
return existingAccount.getBalance();
}
return null;
}
}
The class of which objects are to be logged:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Account
{
#Id
#GeneratedBalance
protected long id;
Double balance;
}
The logging class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class ChangeAccountLog
{
#Id
#GeneratedBalance
private long id;
private Double oldBalance;
private Double newBalance;
}
You might want to use Hibernate Envers to create a versioning table instead of creating separate log objects.
I have two tables Employee and Address as shown:
public class Employee {
private Integer id;
private String firstName;
private String lastName;
private boolean employeeStatus;
private Address address;
//getters setters
}
public class Address {
private Integer id;
private String country;
private String city;
private String street;
private Integer emp_id;
//getters setters
}
#Repository("employeeDao")
public class EmployeeDaoImpl implements EmployeeDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public void insertEmployee(Employee e)
{
String sql = "INSERT INTO tbl_employee (dept_id,firstName,lastName,employeeStatus) values(?,?,?,?,?)";
this.jdbcTemplate.update(sql,new
Object[]{e.getDept_id(),e.getFirstName(),e.getLastName(),e.isEmployeeStatus()});
// INSERT ADDRESS????
}
// Other Methods
}
Now i want to implement Transactional while inserting the employee and address table attributes. I am abit confused here. Does #transactional annotation over the method does the required job? So far i understood that. Also, is it best practice to insert address from where i am inserting employee attributes? I also read somewhere that the transactional should be implemented from service layer than Dao. How would transactional can be implemented in this case?
EDIT
Since it is recommended to use #transactional in service layer, service layer became like:
#Service("employeeService")
#Transactional
public class EmployeeServiceImpl implements EmployeeService{
#Autowired
EmployeeDao employeeDao;
#Autowired
AddressDao addressDao;
#Override
public void insertEmployee(Employee e) {
employeeDao.insertEmployee(e);
addressDao.insertAddress(e.address);
}
}
is it the right way to perform transactional? Also can anyone explain #Transactional(propagation = Propagation.SUPPORTS, readOnly = true) instead of plain #Transactional ?
Alltough the #Transactional annotation would do the job, transactions are usually defined on service level. This way one business call is in one transaction, making sure everything succeeds or fails together.
you can read about #transactional in combination with jdbctemplate here