Hibernate envers add audit column (username) - java

Requirement is to add username column for every audit entry.
After googling a lot and going through the enverse docs I figured out how to implement it. I have implemented it as follows:
Implemented a revision listener:
#Configurable
public class UserRevisionListener implements RevisionListener
{
public void newRevision(Object revisionEntity)
{
UserRevEntity revision = (UserRevEntity) revisionEntity;
String username = "";
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
if (principal != null && principal instanceof DepotUser)
username = ((DepotUser) principal).getUsername();
revision.setUsername(username);
}
}
Create an entity class:
#Entity
#Table(name = "USER_REV_ENTITY")
#RevisionEntity(UserRevisionListener.class)
public class UserRevEntity extends DefaultRevisionEntity
{
private static final long serialVersionUID = 1L;
private String username;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}
I have referred a link that has an example of the same "Thinking in Software ".
Default auditing is working fine.
Firstly, the table for the audit revision with the new username column should get auto generated (I think!). Its not getting generated.
So, I tried by creating the table and adding the table mapping in UserRevEntity. But no luck.
Can anyone help my identifying the issue?
Thanks, in advance.

#MappedSuperclass
public class DefaultRevisionEntity implements Serializable {
#Id
#GeneratedValue
#RevisionNumber
private int id;
#RevisionTimestamp
private long timestamp;
// ... rest of the class body here....
This is DefaultRevisionEntity, which has already declared with id & timestamp.
For your new column username, it has to be annotated as #Column in UserRevEntity as given below.
#Column(name="username")
private String username;

Related

Child and Parent Database - Spring Boot

I have developed two tables in Spring Boot, User and UserMeta. User is the parent and UserMeta is the child table. The foreign-key is user_id. I may be looking at it the wrong way, but I want to be able to first create an entity of User. Then, I want to create an entity of UserMeta. Simply UserMeta should contain additional data to User.
However, when first creating a User and then a UserMeta entity, I get e new User entity (ending up with two User entities and one UserMeta entity.)
The problem I think is that I create a UserMeta object with a User, since I want to have a relationship between User and UserMeta. But if I want to be able to first create a User and then a UserMeta, should I simply ignore a foreign-key? Or, does it exists another way of creating a UserMeta entity without creating a new User?
User
public class User {
#Id
#SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
//#OneToOne(optional=false)
private Long userId;
private String username;
private String password;
private String email;
#OneToOne(mappedBy = "user")
private UserMeta userMeta;
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
}
UserMeta
public class UserMeta {
#Id
#SequenceGenerator(name = "user_meta_sequence", sequenceName = "user_meta_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_meta_sequence")
private Long userMeta_Id;
private String lastname;
private int age;
#OneToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false
)
#JoinColumn(
name = "user_Id",
referencedColumnName="userId"
)
private User user;
public UserMeta(String lastName, int age, User user){
this.lastname = lastName;
this.age = age;
this.user = user;
}
}
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserService
public interface UserService {
User saveUser(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public User saveUser(User user) {
// TODO Auto-generated method stub
return this.userRepository.save(user);
}
UserController
#RestController
public class UserController {
private UserService userService;
public UserController(UserService userService) {
super();
this.userService = userService;
}
#PostMapping("/user")
public ResponseEntity<User> saveUser(#RequestBody User user) {
return new ResponseEntity<User>(userService.saveUser(user), HttpStatus.CREATED);
}
}
UserMetaRepository
public interface UserMetaRepository extends CrudRepository<UserMeta, Long> {
}
UserMetaService
public interface UserMetaService {
UserMeta saveUserMeta(UserMeta userMeta);
}
UserMetaServiceImpl
#Service
public class UserMetaServiceImpl implements UserMetaService{
private UserMetaRepository userMetaRepo;
public UserMetaServiceImpl(UserMetaRepository userMetaRepo) {
super();
this.userMetaRepo = userMetaRepo;
}
#Override
public UserMeta saveUserMeta(UserMeta userMeta) {
return this.userMetaRepo.save(userMeta);
}
}
UserMetaController
#RestController
public class UserMetaController {
public UserMetaService userMetaService;
public UserMetaController(UserMetaService service) {
super();
this.userMetaService = service;
}
#PostMapping("/userMeta")
public ResponseEntity<UserMeta> saveUserMeta(#RequestBody UserMeta userMeta) {
return new ResponseEntity<UserMeta>(this.userMetaService.saveUserMeta(userMeta), HttpStatus.CREATED);
}
}
you should use this constructor in the User class,
public User(String username, String email, String password, UserMeta userMeta) {
this.username = username;
this.email = email;
this.password = password;
this.userMeta = userMeta;
}
now when you save your user the user Meta will be added to your UserMeta table,
If you want to add a user Meta to an existing user you will only need to set the userMeta and save it with a simple userRepository.save(theUpdatedUser)
you can also create userMeta seperately with your code above, and if you want to assign it to a user already in data base or not you can allows use the power of spring data and use simple userRepository.save(userWithTheAssignedMeta)
the same logic applies the other way for metaUser.
The problem here is that your UserMetadata creation logic is using incomplete JSON:
{ "lastName":"foo", "age":1, "user":{ "username":"foo", "password":"bar", "email":"foo-bar" } }
Within this, the problem is the 'user' has all the data, duplicating what was already created the the database, but does not identify it. Since the mapping has cascade.ALL set on it, Spring/JPA will persist the UserMetadata and find this User instance that doesn't have identity, so persist it - giving it identity from the sequence.
There are a few ways you might correct this. First and easiest is to send the User ID in the json from the previously created instance:
{ "lastName":"foo", "age":1, "user":{ "userId":1, "username":"foo", "password":"bar", "email":"foo-bar" } }
This will allow Spring/JPA to recognize the user's identity and merge it and the data provided into the database. It means though that you must send complete data for the User - it will push incomplete data into the DB.
If that is a concern, you can change the cascade options. You may not want cascading persist/merge at all on this relationship, and I suspect when you delete userMetadata you don't really want to delete the User instance, so I think this might have been done incorrectly (maybe put it on the user->UserMetadata relationship instead?). If you remove the cascade settings, spring/JPA will let you just pass in JSON with the USER id specified, as this gives it enough to set the fk:
{ "lastName":"foo", "age":1, "user":{ "userId":1} }

JPA - Error saving entities in OneToOne relationship

I am doing a project for my studies using Spring, Maven, Tomcat, mySQL. I would like to create a website where users can login and update their settings and based on these settings they generate stuff.
At the moment the login is working fine and new users can be registered and saved to the database. Now I created a new entity and made a one to one reletaionship between the two tables - one is the table of the login details like password and username and the other one contains the settings of this user. The issue I am facing:
I have some textfield and combobox in the UI with vaadin - I populate the fields and click save
A binder passes these settings to a service that saves the object
It gets the currently logged in user and loads it from the database
When SQL tries to save the objects it throws error:
Caused by: java.sql.SQLException: Field 'user_login_id' doesn't have a
default value
Here are the entities:
#Entity
#Table(name = "USERLOGIN")
public class UserLogin implements UserDetails {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#OneToOne(mappedBy = "userlogin")
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
**Other getters and setters**
}
#Entity
#Table(name = "USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Integer id;
#Column(name = "meal")
private Integer meal;
#OneToOne
#JoinColumn(name = "userlogin_id")
private UserLogin userlogin;
public UserLogin getUserLogin() {
return userlogin;
}
public void setUserLogin(UserLogin userLogin) {
this.userlogin = userLogin;
userLogin.setUser(this);
**Other getters and setters**
}
The service that saves the settings:
#Service
public class AddUserServiceImpl implements AddUserService{
#Autowired
private UserRepository userRepository;
#Autowired
private CurrentUserService currentUserService;
public void saveUser(User userDAO) {
User user = new User();
user.setMeal(userDAO.getMeal());
user.setUserLogin(currentUserService.getCurrentUser());
userRepository.save(user);
}
}
The repository extends the JPArepository:
#Repository
public interface UserRepository extends JpaRepository<User, Integer>{
}
And finally the service and the repository that loads the currently logged in user:
#Service
public class CurrentUserServiceImpl implements CurrentUserService {
#Autowired
UserLoginRepository userLoginRepository;
public String getCurrentUsername() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
public UserLogin getCurrentUser() {
return userLoginRepository.findByUserName(getCurrentUsername());
}
}
#Repository
public interface UserLoginRepository extends JpaRepository<UserLogin,
Integer> {
#Query("select u from UserLogin u where u.username=:username")
UserLogin findByUserName( #Param("username") String username);
}
Any help would be appreciated I am really new to this topic. So the main goal is that I want to have a table that stores properties for the currently logged in user - these settings should be able to be updated any time. Is there any best practice for this?
Thank you!

Hibernate - mapping an attribute OneToMany. (like OrderLine) Not association

I'm strugling with JPA. I tried several things but I can't figure out the right way to put the annotations.
What is want is like an Order/OrderLine relationship.
Thus:
Order( PK=orderId, fields=[...])
OrderLine (Pk1=orderId,Pk2=orderLineId, fields=[...])
Obviously, OrderLine.orderId refers to the 'Order' table.
What I functionally want to do is at least:
retrieve the Order with and without all orderlines. It should have a Set
retrieve an orderline by full PK, but without the associated Order.
retrieve a list of orderlines by orderId.
I only want these 2 tables and classes. nothing more nothing less.
I tried several things. Can anybody help me out with putting in the right annotations and members on these two classes?
Edit: what i've done so far.
Note that in this real example User=Order and UserRun=OrderLine. So, i am not interested in a seperate 'Run'-entity. Merely a UserRun as described by the Orderline.
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String runUuid;
public UserRunKey(){};
public UserRunKey(String userName, String runUuid) {
this.runUuid = runUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return runUuid;
}
}
This created a userruns/orderline table with the PK in the wrong way:
create table user_runs (run_uuid varchar(255) not null, username varchar(255) not null, primary key (run_uuid, username))
I want the primary key in reverse.
I want username as FK to User
I want a Set in my User-class.
When I do the following in my User-class:
#OneToMany
private Set<UserRun> userRuns;
It will create a
create table user_user_runs (user_username varchar(255) not null, user_runs_run_uuid varchar(255) not null, user_runs_username varchar(255) not null, primary key (user_username, user_runs_run_uuid, user_runs_username))
And that's something I definitely don't want! Once again, I don't want a Run-object (same as nobody's interested in a Line-class, from OrderLine)
I think I figured it out.
The UserRun/Orderline class:
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String zrunUuid; //starts with a z, so the PK will be pk(username,run_uuid). Apparently, order in PK is determined from the variable names (alphabetic order)....
public UserRunKey(){};
public UserRunKey(String userName, String zrunUuid) {
this.zrunUuid = zrunUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return zrunUuid;
}
}
In the userclass:
#OneToMany(mappedBy = "id.userName", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<UserRun> userRuns;
Unfortunately, there are 2 downsides:
I see that there are 2 queries executed instead of a Join on username. One to retrieve user, and 1 to retrieve the Set...
I needed to alter variablenames of the PK (compound/Embeddable). It seems there is no clean way to define the PK order. (Seriously?). Fortunately, the variable name is private, and not exposed by getter.
If anybody knows a cleaner way for these 2 issues. Let me know!
I think what you have to do is the following:
Because the primary key is compound key you need an ID class, as you already did:
#Embeddable
class OrderLinePK implements Serializable {
// you can use physical mapping annotations such as #Column here
#Column(name="...")
private Integer orderLineID;
// This is foreign key and the physical mapping should be done
// on the entity, and not here
private Integer orderID;
public OrderLinePK(){}
// getters + setters
// orverride equals() and hashCode() methods
}
Implement OrderLine entity
#Entity
public class OrderLine {
#EmbededId private OrderLinePK id;
#Mapsid("orderID")
#ManyToOne
#JoinColumn(name = "ORDER_ID", referencedColumn="ID")
private Order order;
// getters + setters ....
}
And the Order entity:
#Entity
public class Order {
#Id
private Integer id;
#OneToMany(fetch = FetchType.LAZY) // actually default by 1-to-n
private Coolection<OrderLine> orderLines;
// getters + setters ....
}

LdapRepository update spring-ldap

Spring LdapRepository save() method throws exception when I'm trying to update an existing object in LDAP database.
org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException: ERR_250_ENTRY_ALREADY_EXISTS
What method should I use to update existing ldap objects?
Person class:
#Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })
public class Person implements Serializable {
public Person() {
}
#Id
private Name dn;
#Attribute(name = "cn")
#DnAttribute(value = "cn")
#JsonProperty("cn")
private String fullName;
#Attribute(name = "uid")
private String uid;
private String mail;
#Attribute(name = "sn")
private String surname;
//setters and getters
}
Person repo interface:
public interface PersonRepo extends LdapRepository<Person> {
}
That's how I'm updating person:
personRepo.save(person);
Default implementation for Spring LDAP repositories is SimpleLdapRepository, that checks the property annotated with #Id to determine if the objects is new - and perform create, or old - and perform update.
I'm guessing that Person.dn is null when you're trying to perform update.
You also can take the control over this by implementing org.springframework.data.domain.Persistable and place your logic in the isNew() method.
See the implementation details.

Relationships in Google App Engine

I have just one week experience in GAE/Java and trying to port an legacy application(developed using PHP/MySQL) to GAE+JDO. I'm now stuck with a basic issue in creating a relationship between two tables(kinds in GAE).
So here is the case:
We have a Users table which holds the user authentication information. It also has a field user_role which store role_id, which is actually a foreign key of another table user_roles.
From the the Entity-Relationship documentation in GAE, I understand that DataStore doesn't support foreign-key relationships and designed the Users class by adapting the Employee-ContactInfo example in the docs.
When I executed the application, the user_roles kind is inserted each time I add an entry in Users table. The user_roles kind is supposed to have only three static values. But this is having redundant values as I input more records in Users.
I think that I'm missing something very trivial, but I couldn't figure it out due to my inexperience to datastore. It would be very nice if someone could guide me to solve this issue.
Here is the code:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Users {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String userName;
#Persistent
private String password;
#Persistent
private String salt;
#Persistent
private Date createdDate;
#Persistent
private Key createdBy;
#Persistent
private Date lastLogin;
#Persistent
private boolean status;
#Persistent
private String authKey;
#Persistent(defaultFetchGroup="true")
private SecurityRole securityRole;
#Autowired
SecurityRepository securityRepository ;
public SecurityPrincipals(String userName, String password,SecurityRole securityRole,boolean status) {
this.securityRole = securityRole;
this.userName = userName;
this.password = password;
this.status = status;
}
//getters and setters
}
Definition for Roles:
#PersistenceCapable(detachable="true")
public class SecurityRole {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String securityRoleName;
#Persistent
private String securityRoleDescription;
#Persistent
private String securityRoleStatus;
#Persistent
private Date securityRoleCreatedDate;
public SecurityRole(String securityRoleName, String securityRoleDescription, String securityRoleStatus,String securityBaseType)
{
this.securityRoleName = securityRoleName;
this.securityRoleDescription = securityRoleDescription;
this.securityRoleStatus = securityRoleStatus;
this.securityBaseType = securityBaseType;
}
// getters and setters
}
The relevant code from Controller:
SecurityRole securityRole = securityRepository.getSecurityRole( securityRoleName);
users = new Users(userName,password,status,securityRole);
iUserRepository.save(employeeDetails);
Here is the definition of getSecurityRole:
public SecurityRole getSecurityRole(String securityRoleName)
{
PersistenceManagerFactory pmf = this.jdoTemplate.getPersistenceManagerFactory();
PersistenceManager pm = pmf.getPersistenceManager();
try {
Query query = pm.newQuery( SecurityRole.class);
query.declareImports("import java.lang.String");
query.declareParameters("String securityRoleName");
query.setFilter("this.securityRoleName == securityRoleName");
List<SecurityRole> securityRoles = (List<SecurityRole>)query.execute(new String(securityRoleName));
SecurityRole temp = null;
for(SecurityRole securityRole: securityRoles)
{
temp = securityRole;
}
return temp;
}
finally {
pm.close();
}
}
Here is the definition of iUserRepository.save():
public void save(Users user) {
jdoTemplate.makePersistent(companyDetails);
}
In the Users class, you have the defined the property
#Persistent(defaultFetchGroup="true")
private SecurityRole securityRole;
This statement creates an "owned" relationship in GAE datastore, which means that when you create an object of the Users class, an object of the SecurityRole class will be created as well.
What you need, is an unowned relationship that can be created as follows:
#Persistent(defaultFetchGroup="true")
private Key securityRole;
In this way, a SecurityRole object is not created each time you create an instance of the Users class. For more information about owned and unowned relationships, take a look at http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html
Hope this helps!

Categories