I've recently tried to implement Spring Security into my web store project to distinguish between single users. Websites are working properly except there is one issue which I can't track to resolve. I have object called Customer within User class. Customer object has fields like id, balance, etc., and User has OneToOne relationship to Customer, so I can have single object for credentials and foreign key to specifics of user - his first name, last name, balance, owned products, etc.
I also have Product class which has ManyToOne relationship with Customer. It has its' own id, productCost, etc.
I'm using Spring MVC to take care of proper URL dispatching. When some action is taken, I'm using #AuthenticationPrincipal annotation to get currently logged Customer (through foreign key in User) and modify data regarding Customer linked with that foreign key.
When I modify Customer data through #AuthenticationPrincipal in controller, changes are immediate and they show up on website. But when I try to modify data through some DAO, for example by searching for Customer through id or try to get Customer that owns Product from Product getter (ManyToOne has reference to owning Customer), changes are not immediate. Database updates itself immediately and properly, like in first case, but collections in code and website state are not changed until I logout and login again - that's when data is updated. I suspect it may be due to fact that updating UserDetails updates data directly for currently logged user but then - how may I achieve same effect for Customer found by id?
Snippets of code:
Users.java:
#Entity
#Table(name="users")
public class Users {
#Id
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#Column(name="enabled")
private boolean isActive;
#OneToMany(mappedBy="user")
private Set<Authorities> authorities;
#OneToOne
#JoinColumn(name="customer_id")
private Customer customer;
Product.java:
#Entity
#Table(name="product")
public class Product {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String productName;
#Column(name="description")
private String productDescription;
#Column(name="category")
private String productCategory;
#Column(name="cost")
private int productCost;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="owner_id")
private Customer productOwner;
Customer.java:
#Entity
#Table(name="customer")
public class Customer {
//Class fields
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="balance")
private int balance;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
#OneToMany(mappedBy="productOwner", fetch=FetchType.EAGER)
private List<Product> ownedProducts;
Piece of controller code:
#Autowired
CustomerService customerService;
#Autowired
ProductService productService;
/*(...)*/
#GetMapping("/showOffer/{offerId}")
public String getOffer(#PathVariable int offerId, Model theModel, #AuthenticationPrincipal MyUserDetails user) {
Product retrievedProduct = productService.findById(offerId);
if (user.getCustomer().getBalance() >= retrievedProduct.getProductCost())
{
Customer retrievedProductOwner = retrievedProduct.getProductOwner();
/* This is where changes aren't applied immediately and I need to logout and login to process them. */
retrievedProductOwner.setBalance(1000);
/* This is where changes are immediately shown and Java collections are updated: */
user.getCustomer().setBalance(user.getCustomer().getBalance()-retrievedProduct.getProductCost());
/* Code below is an attempt to force immediate changes by updating collections directly from database - but that approach doesn't work */
productService.delete(retrievedProduct.getId());
retrievedProduct.getProductOwner().getOwnedProducts().clear();
retrievedProduct.getProductOwner().setOwnedProducts(productService.listOwnerProducts(retrievedProduct.getProductOwner()));
}
else {
System.out.println("Insufficient funds!");
}
return "redirect:/home";
TL:DR
I use UserDetails object in controller and I am also using DAO for Customer used as foreign key in UserDetails. Using UserDetails directly updates data and everything works fine, using DAO doesn't make changes until I logout and login.
as far as i understand your changes are only commited when you log out .
just try to synchronize and commit any modification at the right time and it would be safer that you manage sessions and transactions at the same time so you don't get any sort of incoherence when you do that. then tell me about the results .
Check whether CTRL+F5 in your browser (force cache clearance) updates your data similarly to logging out and back in. If so, it's a question of cached information. (this and (3) may occur at the same time)
Alternatively ... or perhaps complementarly ... your data fetch reqeust may be called before the database update/commit operation is completed. If so, it should become evident if you run distinct update and show routines. i.e. turn A into B, then into C, and you'd get something like B when you're expecting C... A instead of B... etc.
Lastly, depending on how you set up your back end, it is possible that you only populate whatever form you use for the front end exactly once, instead of dynamically querying the database whenever you access that form.
Related
I have a question regarding mapping entities based on mapping in their tables.
So, we are a team of five working on a project, one of our team mate seem to add mapping between tables in opposite direction, I'll give examples of both to let you understand what I mean.
We have two tables User and UserInfo. UserInfo has a user_id as foreign key.
1) Common Mapping I have learnt about in hibernate.
In User and UserInfo entities I usually have mappings like this:
class User{
private int userId;
private String userName;
// getter and setters
}
class UserInfo{
private int userInfoId;
private String firstName;
private String lastName;
#OneToOne()
#JoinColumn(name="user_id")
private User user;
}
2) This is how my colleague does mapping:
class User{
private int userId;
private String userName;
#OneToOne(mappedBy="user")
#JoinColumn(name="user_id")
private UserInfo userInfo;
// getter and setters
}
class UserInfo{
private int userInfoId;
private String firstName;
private String lastName;
#OneToOne()
private User user;
}
He does just opposite of what I learnt from tutorials. It is working fine somehow but I am not sure if this is the right way to map two entities.
Please help.
Thanks.
both of them should create exact same tables in DB, but second solution is better when u need call user from userInfo or userInfo from user.
ex:
User user = ...
user.getUserInfo().getFirstName();
UserInfo info = ...
info.getUser().getUserName();
PS:
In this article says that most efficient to use #OneToOne with #MapsId
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
The 2nd code snippet will absolutely work fine because you are not using a bi-directional relationship.
In case if you get JsonMappingException then simply you can handle by using below
annotation
You may use #JsonIgnore
The difference is that the one your colleague used is bidirectional and yours is unidirectional.
Bidirectional association provides navigation in both the directions. If you need userInfo object when you query user then bidirectional is what you need. If that's not the case, the one you have is more efficient than your colleague's.
I would recommend you to read this useful link on how to do one-to-one mapping efficiently:
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
Say you are creating a new entity of the type User, User has the nested object Billing given that you know that a Billing exists with the ID 1, is there a simple way with which you can form an association between a new User and an existing Billing?
Assume that fetching a Billing object to set to the user is an expensive operation, therefore the solution of fetching the entire Billing object and setting it to the user is not an option.
My question is, Is there a short hand method of saving this relationship between an entity and its nested counterpart, using spring data?
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int userId;
private String name;
#ManyToOne
#JoinColumn(name = "billing_id")
private Billing userBill;
// constructor
// getters and setters
}
For example in sudo code:
User bob = new User();
bob.billingId.id = 1;
userRepository.save(bob);
Absolutely.
JpaRepository.getOne(id) (as opposed to CrudRepository.findById) will call EntityManager.getReference(entityType, id) internally, which is the method designated to handle this exact use case (getting the reference to an entity, without loading its associated state).
To answer your question, what you want is: customer.setBilling(billingRepository.getOne(billingId)).
I am wondering about best practices in database design with Hibernate.
I have a User entity that is going to have a lot of different settings. For each set of settings, I have to either add them as extra columns in the User table or create a separate entity and connect them with a #OneToOne relationship. It is my understanding that #OneToMany and #ManyToOne relationships should generally take place in separate tables because you should not have columns that are optional.
But it is kind of unclear for #OneToOne relationships. I think there is a case for using #OneToOne because ORMs will select all single attributes by default and having a lot of columns will slow down that process.
An example of what I am talking about can be illustrated by
#Entity
public class User{
#OneToOne
private ForumSettings forumSettings;
#OneToOne
private AccountSettings accountSettings;
#OneToOne
private SecuritySettings securitySettings;
}
vs
#Entity
public class User{
#Column
private boolean showNSFWContent; //Forum Setting
#Column
private int numberOfCommentsPerPage; //Forum Setting
#Column
private boolean subscribedToNewsLetter; //Account Setting
#Column
private boolean isAccountBanned; //Account Setting
#Column
private boolean isTwoFactorAuthenticationEnabled; //Security Setting
#Column
private boolean alertForSuspiciousLogin; //Security Setting
}
The above is a simple example to show the concept, but in practice there would be many more columns in the 2nd portion.
I know that this might be opinion based, but I am hoping someone could share the pros/cons of both choices.
Thank you very much
Your question is in general about Data normalization. Normalization is itself extensive field of study and basically is a way of structuring database tables avoiding redundancy and making sure that updates don’t introduce anomalies.
And first rule of normalization says a table shall contain no repeating groups. In your case it does.
SOLUTION 1 : Store UserSettings as Entity as map as OneToMany relationship
#Entity
public class User
#OneToMany
private List<UserSettings> userSettings;
And then you can query for particular setting type by joining User and UserSettings entities.
For example (JPQL)
SELECT user u
JOIN u.settings us
WHERE us.settings_type = 'account_settings'
and us.settings_value = 'secure' // or any other logic
Advantage of this approach is that UserSettings will have it is own persistence identity and can be queried by it's own. It it is not dependent on parent.
For example :
SELECT q from Query q where ...
Solution 2 : Store settings in a collection of basic elements
You can store User Settings in the collection (Each user will have it's own set of settings)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#ElementCollection
#CollectionTable(name="USER_SETTINGS")
#MapKeyColumn(name="SETTINGS_TYPE")
#Column(name="SETTINGS_VALUE")
Map<String, Boolean> userSettings = new HashMap<>();
UserSettings collection will be stored in a separate table with foreign key to User table. UserSettings does not have it is own persistence ID, is dependent on User entity and can be queried only through it is parent ('User')
Solution 3: Store User Settings as Embedded type
Embedded type is not an entity, it does not have it is own persistence ID and is depends on parent type, stored as part of parent record in database (in User table)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#Embedded
private UserSettings userSettings;
UserSettings is in separate class, but stored in User table.
#Embeddable
public class UserSettings {
private List<String> securitySettings; // or any other collection type
private List<Boolean> forumSettings;
I am trying to use the #SQLDelete annotation of Hibernate to make soft deletion. It works well when the DB schema is static, i.e: passing it in the SQL.
Unfortunately, it seems the SQL is passed as is to EntityPersisters (cf EntityClass's method CustomSQL createCustomSQL(AnnotationInstance customSqlAnnotation) so I can't find a way to pass the schema name dynamically like in Native SQL queries using {h-schema}
Did anyone find a good workaround for this issue (I am using Hibernate 4.3.5)?
Edit: Unless there is a real solution, I ended up modifying the code source of org.hibernate.persister.entity.AbstractEntityPersister by replacing the schema placeholder when setting the custom SQL queries in method doLateInit.
Edit2: I have created an issue for this behaviour in Hibernate JIRA. I will create a pull request later today and I wish the Hibernate Team will accept it
Soft deletes using Hibernate annotations.
As linked author stated below:
I am currently working on a Seam application that has a need for soft deletes in the database. To the right you can see a snippet of my database diagram which contains a CUSTOMER and APP_USER table. This is just a straight forward one to many relationship but the important thing to note though is the “DELETED” field in each table. This is the field that will be used to track the soft delete. If the field contains a ‘1’ the record has been deleted and if it contains a ‘0’ the record hasn’t been deleted.
Before ORMs like Hibernate I would have had to track and set this flag myself using SQL. It wouldn’t be super hard to do but who wants to write a bunch of boilerplate code just to keep track of whether or not a record has been deleted. This is where Hibernate and annotations comes to the rescue.
Below are the 2 Entity classes that were generated by Hibernate using seamgen. I have omitted parts of the code for clarity.
Customer.java
//Package name...
//Imports...
#Entity
#Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
private Billing billing;
private String name;
private String address;
private String zipCode;
private String city;
private String state;
private String notes;
private char enabled;
private char deleted;
private Set appUsers = new HashSet(0);
// Constructors...
// Getters and Setters...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "customer")
// Filter added to retrieve only records that have not been soft deleted.
#Where(clause = "deleted <> '1'")
public Set getAppUsers() {
return this.appUsers;
}
public void setAppUsers(Set appUsers) {
this.appUsers = appUsers;
}
}
AppUser.java
//Package name...
//Imports...
#Entity
#Table(name = "APP_USER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE app_user SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class AppUser implements java.io.Serializable {
private long id;
private Customer customer;
private AppRole appRole;
private char enabled;
private String username;
private String appPassword;
private Date expirationDate;
private String firstName;
private String lastName;
private String email;
private String phone;
private String fax;
private char deleted;
private Set persons = new HashSet(0);
// Constructors...
// Getters and Setters...
}
The following 2 steps is all that I had to do to implement the soft delete.
Added the #SQLDelete annotation which overrides the default
Hibernate delete for that entity.
Added the #Where annotation to filter the queries and only return
records that haven’t been soft deleted. Notice also that in the
CUSTOMER class I added an #Where to the appUsers collection. This is
needed to fetch only the appUsers for that Customer that have not
been soft deleted.
Viola! Now anytime you delete those entities it will set the “DELETED” field to ‘1’ and when you query those entities it will only return records that contain a ‘0’ in the “DELETED” field.
Hard to believe but that is all there is to implementing soft deletes using Hibernate annotations.
Note:
also note that instead of using the #Where(clause="deleted ‘1’") statements you can use hibernate filter (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-filters) to globally filter-out all ‘deleted’ entities. I found that defining 2 entity managers (‘normal’ one that filter deleted items, and one that doesn’t, for the rare cases…) is usually quite convenient.
Using EntityPersister
You can create a DeleteEventListener such as:
public class SoftDeleteEventListener extends DefaultDeleteEventListener {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void onDelete(DeleteEvent event, Set arg1) throws HibernateException {
Object o = event.getObject();
if (o instanceof SoftDeletable) {
((SoftDeletable)o).setStatusId(1);
EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), o);
EntityEntry entityEntry = event.getSession().getPersistenceContext().getEntry(o);
cascadeBeforeDelete(event.getSession(), persister, o, entityEntry, arg1);
cascadeAfterDelete(event.getSession(), persister, o, arg1);
} else {
super.onDelete(event, arg1);
}
}
}
hook it into your persistence.xml like this
<property name = "hibernate.ejb.event.delete" value = "org.something.SoftDeleteEventListener"/>
Also, don't forget to update your cascades in your annotations.
Resource Link:
Hibernate: Overwrite sql-delete with inheritace
Custom SQL for CRUD operations
Custom SQL for create, update and delete
Use like this
#SQLDelete(sql = "UPDATE {h-schema}LEAVE SET STATUS = 'DELETED' WHERE id = ?", check = ResultCheckStyle.COUNT)
I think there are 2 way
First is to add:
app.datasource.schema=<schema_name>
to your application.properties.
The second is to use the schema in annotation to your table model
I've used Hibernate / JPA in the past, now using a combination of Spring JDBC and MyBatis.
With JPA/ Hibernate if you had a Customer, which had an address you would have a domain structure similar to code below. (minus all the annotations / config / mappings).
Does this still make sense when using JDBC or MyBatis. This is composition domain design from what I know, has-a, belongs-to, etc. However most examples I've seen of JDBC code they have domain object that bring back the IDs rather than collection, or flatten the data. Are there any performance benefits from either approach, maintainability, etc. Having worked with JPA first I'm not sure what the JDBC way of doing things are.
public class Customer {
private Long id;
private String userName;
private String password;
private String firstName;
private String lastName;
private Collection<Address> addresses
...
}
public class Address {
private Long id;
private String streetAddress1;
private String streetAddress2;
private String city;
private State state;
private String postalCode;
}
public class State {
private Long id;
private String code;
private String name;
private Country country;
}
public class Country {
private Long id;
private String code;
private String name;
}
I come across an example and here was one of their classes.
public class Question {
private long questionId;
private long categoryId;
private long userId;
private long areaId;
private String question;
private String verifyKey;
private Date created;
private User user;
private List<Answer> answers;
private long answerCount;
private String name;
// getters and setters omited...
}
Why would you fetch the userId, areaId, and categoryId instead of actually fetching the associated object? The ID is likely of no use to the front end user, I suppose you could use the ID to issue another query to fetch additional data, but seems inefficient making another round trip to the database.
You can look at this domain object as a "footprint" of database table. In your example, userId, areaId and categoryId from Question are most likely foreign keys from corresponding tables. You could never need full object data in the moment of Question creation and retrieve it later with separate db request. If you fetch all associated objects at once, you will hit at least one additional table per object (by join-s or subselect-s). Moreover, that's actually the same that Hibernate does. By default, it loads domain object lazily and hits database again if uninitialized associated object is needed.
At that time, it is better to fetch those objects that a domain object can't exist without. In your example, Question and List are coupled.
Of course, if you need user, or category, or any other associated object again in some another place of application (assume the reference to previously retrieved object has been lost), you will hit the database with same query. It should be done and could seem inefficient, because both plain JDBC and SpringJDBC have no intermediate caching unlike Hibernate. But that's not the purpose JDBC was designed for.