Hibernate: Temporarily exclude a column from and update - java

Lets say I have a table like this
USER_ID USERNAME PASSWORD DATE_CREATED DATE_LAST_LOGIN
1 'user1' 'password1' '12-Jun-2010' '12-Nov-2010'
2 'user2' 'password2' '14-Jun-2010' '12-Nov-2010'
Which is mapped to a POJO class using hibernate like this
#Entity
#Table( name="user" )
public class User {
private Integer id;
private String username;
private String password;
private Date dateCreated;
private Date dateLastLogin;
public User() {
}
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
public IntegergetId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Colum( name="USERNAME" )
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Colum( name="PASSWORD" )
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password= password;
}
#Colum( name="DATE_CREATED" )
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated= dateCreated;
}
#Colum( name="DATE_LAST_LOGIN" )
public Date getDateLastLogin() {
return dateLastLogin;
}
public void setDateLastLogin(Date dateLastLogin) {
this.dateLastLogin = dateLastLogin;
}
}
This class will be used in two ways,
1. Authenticate a user for login and when the user logs in the DATE_LAST_LOGIN will be updated with the current date.
2. Update user details in a user edit form.
My problem is that I only ever want to update the DATE_LAST_LOGIN field when the user logs in, not when editing a user in the form. If I were to load a User record and then save it without calling setDateLastLogin then that would be fine until the time comes that a user logs in in-between the load and update operations. This will cause the DATE_LAST_LOGIN to be updated in the db but then when the user edit form saves the changes it will override the DATE_LAST_LOGIN with the old, incorrect value.
EDIT:
I don't think I explained the situation fully so here is some more info .... This is specifically in a webapp, the update of the user happens like so.
User record loaded and used to populate an html form
The form is edited and submitted back to the server
The server commits the changes to the database
As it stands I to not query the object from the database again before updating on form submission, i just populate a new object with the fields from the form and save it to the database, this of course means that the dateLastLogin is null at the time of saving the update and so would replace the correct value with null in the DB. This obviously could not be solved by simply synchronizing threads. Before hibernate I would simply have chosen not to update the DATE_LAST_LOGIN field but hibernate is not allowing me to make that design time decision.
An alternative would be to query the object before overwriting with the fields from the form, however this forces me to have to run a query before the update which I would not have to do without hibernate, and this would force me to use the synchronization that was suggested in one answer. The problem with that is that synchronization would only apply in the current application, if I had multiple apps updating the same DB then it would be useless.
Which comes back to my original question, is there a way to exclude the field from the update, if not by simply asking hibernate to do so then perhaps by a different system design.

My suggestion would be to use 2 different entities for the user, e.g. User and UserLogin (where the later would extend the regular User and hold the dateLastLogin property) and to use UserLogin during authentication and the short User when editing the user details.

This is a synchronization problem more than an Hibernate one. You should make your two operations (login and edition of user) atomic with respect to each other. That can be achieved by simple Java synchronization (common mutex or lock before entering these operations Java methods) or by database (i.e. Hibernate) transaction management.

Can you control this with a switch (boolean) value that you can toggle as needed and then add this check to the setter?
eg:
boolean dateLastLoginUpdatable = false;
public void setDateLastLogin(Date dateLastLogin) {
if( dateLastLoginUpdatable ) {
this.dateLastLogin = dateLastLogin;
}
}
public void toggleLastLoginEditable( boolean newValue ) {
dateLastLoginUpdatable = newValue;
}

Ok so I came up with my own solution which works for me though I don't know if there is not a better solution at present.
Basically in my POJO I have made the getDateLastLogin field read only by adding updatable = false to the annotation like so
#Colum( name="DATE_LAST_LOGIN", updatable = false )
public Date getDateLastLogin() {
return dateLastLogin;
}
Then when logging in I bypass the POJO and update the database directly with an HQL update in my login controller a little bit like this
public void login(String username, String password) {
User user = getUser( username, password );
if( user != null ) {
Query userUpdate = getSession().createQuery( "update User set dateLastLogin = current_timestamp where id = :userId" );
userUpdate.setInteger( "userId", user.getId() );
userUpdate.executeUpdate();
}
}

Related

BeanPropertyRowMapper not converting mysql tinyint 1 to boolean true

public class Foo {
private long id;
private String name;
private boolean isBar;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isBar() {
return isBar;
}
public void setBar(boolean isBar) {
this.isBar = isBar;
}
}
#Component
public class FooDAO {
private JdbcTemplate jdbcTemplate;
private FooDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<Foo> findAll() {
return jdbcTemplate.query( "SELECT * FROM foo", new BeanPropertyRowMapper<>(Foo.class);
}
}
When I setup a custom FooRowMapper and manually call setBar(rs.getBoolean("is_bar")) Foo.isBar is properly getting set to true when db value is 1, but not when using the BeanPropertyRowMapper instead of a custom row mapper.
According to this, BeanPropertyRowMapper should properly convert 1 to true, so why isn't it in my case?
p.s. I already figured out why but thought I'd post it in case it's helpful to anybody. I'm sure it won't take long for someone else to figure it out and post the answer.
I knew this:
Column values are mapped based on matching the column name as obtained from result set meta-data to public setters for the corresponding properties. The names are matched either directly or by transforming a name separating the parts with underscores to the same name using "camel" case.
But got thrown off because my Foo.isBar property had the correct camel case equivalent of my db field name (is_bar), however, my public setter name was incorrect as setBar; the setter should be setIsBar.
After googling I was also thrown off by others wanting to use BeanPropertyRowMapper to convert database values of Y/N to boolean values.
And I also assumed BeanPropertyRowMapper was actually setting the value to false even though it wasn't and the false value simply remained as the default boolean primitive value.
Another solution if for whatever reason setBar instead setIsBar was actually desired would be to use an field alias in the sql select statement like it says in the docs:
To facilitate mapping between columns and fields that don't have matching names, try using column aliases in the SQL statement like "select fname as first_name from customer".

Java database connect in 2 classes

Hi everyone in my project I have 2 classes a Login Class and a Sports Class. And I want to pull a specific field from the database in the Sports Class based on the user login.
So for example if my login is 12345 the database should pull the respective sport name based on my login.
How do I connect the 2 classes in such a way so that the database can pull data based on my login in the previous class?
You can have a singleton class and store user details in that class when login happened and later you can access that from any other class.
public class UserDetails{
private static UserDetails instance = new UserDetails();
private UserDetails(){}
private String userId;
public static UserDetails getInctance(){
return instance;
}
public String getUserId(){
return userId;
}
public void setUserId(String userId){
this.userId = userId;
}
}
Then in Login class,
UserDetails.getInctance().setUserId("12345");
In Sports Class,
String userId = UserDetails.getInctance().getUserId();
Then user userId in query.
I am assuming time of calling you have the user information.Then simply you can get the userid and pass as method argument to sports detail method.Try to make classes loosely coupled.

why not hierarchy from entity to create dto?

Is there any reason to do not use hierarchy from an entity/model in order to create a dto/form object which help you to hold form search fields?
This is not a big system and these approach will help us to create real dto later if it is needed.
Our models are simple POJO's with almost any logic, maybe some validation logic but that would be valid also for the DTO.
I do not make sense to create a new DTO object with all the fields.
public class User {
private String name;
private String email;
private Date onboardingDate;
public User() {}
public User(String name, String email, Date onboardingDate) {
this.name = name;
this.email = email;
this.onboardingDate = onboardingDate;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Date getOnboardingDate() { return onboardingDate; }
public void setOnboardingDate(Date onboardingDate) { this.onboardingDate = onboardingDate; }
}
my DTO class, I can use it for use creation and for search purpose.
public class UserDTO extends User {
private Date fromDate;
private Date toDate;
public Date getFromDate() { return fromDate; }
public void setFromDate(Date fromDate) { this.fromDate = fromDate; }
public Date getToDate() { return toDate; }
public void setToDate(Date toDate) { this.toDate = toDate; }
public User convertToEntity() {
return new User(super.getName(), super.getName(), super.getOnboardingDate());
}
}
Thanks fox!
Usually, a DTO will be a subset of the entity data or also contain data from other associations in sub-DTOs or directly embedded in that DTO. If the DTO extends the entity, a user of a DTO object will have the possibility to invoke a getter to access all that state.
If your DTO is really a DTO, it will only have a subset of the data, but by extending from the entity, it might happen by accident that you access data that wasn't part of the subset that was loaded.
Imagine your user entity has detailed contact and address information. For one use case, you need that data, but for another you don't. It would not make sense to expose getters/setter for state that isn't there, would it? This is why one usually creates a separate DTO class for that purpose. You can still work with the entity type if you want to persist/update data, but even for these use cases, people sometimes tend to use DTOs because the persistent state does not necessarily represent the state which can be updated in a use case. This is especially important when you have state for e.g. denormalizations in your persistent state or cross cutting concerns like statistics or audit data.
If your model is so simple and will stay this way, then just use the entity model. If in 90% of the your use cases you need all data anyway, there is nothing you can gain from using DTOs.
Considering you have the need to create a subset of the entity state for your use cases I can only recommend you not to extend from the entity model and really just model what your use case requires. Never expose accessors to state that isn't there in DTOs. That will save you hours of debugging later.
Of course you could use your DTO for filter purposes, that's what is usually called filter by example, but you will notice that this has certain limits and quirks, so at some point you will need a different approach.
You can make use of a library that I develop called Blaze-Persistence Entity Views which allows you to create DTOs as interfaces. This is not only an easier way to model DTOs, but it will also perform better because it will only fetch the state really necessary for the desired representation.

How to create a one-to-many relationship with JDBI SQL object API?

I'm creating a simple REST application with dropwizard using JDBI. The next step is to integrate a new resource that has a one-to-many relationship with another one. Until now I couldn't figure out how to create a method in my DAO that retrieves a single object that holds a list of objects from another table.
The POJO representations would be something like this:
User POJO:
public class User {
private int id;
private String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Account POJO:
public class Account {
private int id;
private String name;
private List<User> users;
public Account(int id, String name, List<User> users) {
this.id = id;
this.name = name;
this.users = users;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
The DAO should look something like this
public interface AccountDAO {
#Mapper(AccountMapper.class)
#SqlQuery("SELECT Account.id, Account.name, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
public Account getAccountById(#Bind("id") int id);
}
But when the method has a single object as return value (Account instead of List<Account>) there seems to be no way to access more than one line of the resultSet in the Mapper class. The only solution that comes close I could find is described at https://groups.google.com/d/msg/jdbi/4e4EP-gVwEQ/02CRStgYGtgJ but that one also only returns a Set with a single object which does not seem very elegant. (And can't be properly used by the resouce classes.)
There seems to be a way using a Folder2 in the fluent API. But I don't know how to integrate that properly with dropwizard and I'd rather stick to JDBI's SQL object API as recommended in the dropwizard documentation.
Is there really no way to get a one-to-many mapping using the SQL object API in JDBI? That is such a basic use case for a database that I think I must be missing something.
All help is greatly appreciated,
Tilman
OK, after a lot of searching, I see two ways dealing with this:
The first option is to retrieve an object for each column and merge it in the Java code at the resource (i.e. do the join in the code instead of having it done by the database).
This would result in something like
#GET
#Path("/{accountId}")
public Response getAccount(#PathParam("accountId") Integer accountId) {
Account account = accountDao.getAccount(accountId);
account.setUsers(userDao.getUsersForAccount(accountId));
return Response.ok(account).build();
}
This is feasible for smaller join operations but seems not very elegant to me, as this is something the database is supposed to do. However, I decided to take this path as my application is rather small and I did not want to write a lot of mapper code.
The second option is to write a mapper, that retrieves the result of the join query and maps it to the object like this:
public class AccountMapper implements ResultSetMapper<Account> {
private Account account;
// this mapping method will get called for every row in the result set
public Account map(int index, ResultSet rs, StatementContext ctx) throws SQLException {
// for the first row of the result set, we create the wrapper object
if (index == 0) {
account = new Account(rs.getInt("id"), rs.getString("name"), new LinkedList<User>());
}
// ...and with every line we add one of the joined users
User user = new User(rs.getInt("u_id"), rs.getString("u_name"));
if (user.getId() > 0) {
account.getUsers().add(user);
}
return account;
}
}
The DAO interface will then have a method like this:
public interface AccountDAO {
#Mapper(AccountMapper.class)
#SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
public List<Account> getAccountById(#Bind("id") int id);
}
Note: Your abstract DAO class will quietly compile if you use a non-collection return type, e.g. public Account getAccountById(...);. However, your mapper will only receive a result set with a single row even if the SQL query would have found multiple rows, which your mapper will happily turn into a single account with a single user. JDBI seems to impose a LIMIT 1 for SELECT queries that have a non-collection return type. It is possible to put concrete methods in your DAO if you declare it as an abstract class, so one option is to wrap up the logic with a public/protected method pair, like so:
public abstract class AccountDAO {
#Mapper(AccountMapper.class)
#SqlQuery("SELECT Account.id, Account.name, User.id as u_id, User.name as u_name FROM Account LEFT JOIN User ON User.accountId = Account.id WHERE Account.id = :id")
protected abstract List<Account> _getAccountById(#Bind("id") int id);
public Account getAccountById(int id) {
List<Account> accountList = _getAccountById(id);
if (accountList == null || accountList.size() < 1) {
// Log it or report error if needed
return null;
}
// The mapper will have given a reference to the same value for every entry in the list
return accountList.get(accountList.size() - 1);
}
}
This still seems a little cumbersome and low-level to me, as there are usually a lot of joins in working with relational data. I would love to see a better way or having JDBI supporting an abstract operation for this with the SQL object API.
In JDBI v3, you can use #UseRowReducer to achieve this. The row reducer is called on every row of the joined result which you can "accumulate" into a single object. A simple implementation in your case would look like:
public class AccountUserReducer implements LinkedHashMapRowReducer<Integer, Account> {
#Override
public void accumulate(final Map<Integer, Account> map, final RowView rowView) {
final Account account = map.computeIfAbsent(rowView.getColumn("a_id", Integer.class),
id -> rowView.getRow(Account.class));
if (rowView.getColumn("u_id", Integer.class) != null) {
account.addUser(rowView.getRow(User.class));
}
}
}
You can now apply this reducer on a query that returns the join:
#RegisterBeanMapper(value = Account.class, prefix = "a")
#RegisterBeanMapper(value = User.class, prefix = "u")
#SqlQuery("SELECT a.id a_id, a.name a_name, u.id u_id, u.name u_name FROM " +
"Account a LEFT JOIN User u ON u.accountId = a.id WHERE " +
"a.id = :id")
#UseRowReducer(AccountUserReducer.class)
Account getAccount(#Bind("id") int id);
Note that your User and Account row/bean mappers can remain unchanged; they simply know how to map an individual row of the user and account tables respectively. Your Account class will need a method addUser() that is called each time the row reducer is called.
I have a small library which will be very useful to maintain one to many & one to one relationship.
It also provide more feature for default mappers.
https://github.com/Manikandan-K/jdbi-folder
There's an old google groups post where Brian McAllistair (One of the JDBI authors) does this by mapping each joined row to an interim object, then folding the rows into the target object.
See the discussion here. There's test code here.
Personally this seems a little unsatisfying since it means writing an extra DBO object and mapper for the interim structure. Still I think this answer should be included for completeness!

Save List changes with Hibernate

I have an Object named Token. it has id, name, and value. After saving some data to db, I have loaded them into a web page
_____________________________________________
|____name____|____value____|____operation____|
tkn1 10 ×
tkn2 20 ×
the × sign enable me to delete a token from server collection
now. I have added token tkn3 with value 30 and deleted tkn2 so
the table would be:
_____________________________________________
|____name____|____value____|____operation____|
tkn1 10 ×
tkn3 30 ×
With these changes to the collection, how can I reflect them into database
how to determine the records that deleted, and the records that added?
I applied tow solutions:
I have compared -in business logic layer- the old data with the new data
and find the differences between the then send to database two lists, the first contains
the added tokens and the second contains the ids of tokens to be deleted.
I added a flag named status to the object.. when I add the flag is NEW
when I delete I just set flag to DELETE, and in DB layer I iterate over the collection
one by one object and check the flag.. if NEW then add the record, if DELETE , delete it
and if SAVED (no changes) I do no changes to it..
My questions:
Is this way is good to do this task..?
Is there a Pattern to accomplish this task?
Can Hibernate help me to do that?
• Is this way is good to do this task..?
NO
• Is there a Pattern to accomplish this task?
YES
• Can Hibernate help me to do that?
Hibernate provides the solution for such situation using Cascade Attribute for List property
Refer
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html
http://www.mkyong.com/hibernate/hibernate-cascade-example-save-update-delete-and-delete-orphan/
The blow entity should solve your problem.
#Entity
public class MyEntity {
private static enum Status {
NEW,
PERSISTENT,
REMOVED
}
#Id
private Long id;
private String name;
private int value;
#Transient
private Status uiStatus = Status.NEW;
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
public Status getUiStatus() {
return this.uiStatus;
}
public int getValue() {
return this.value;
}
#PostLoad
public void onLoad() {
this.uiStatus = Status.PERSISTENT;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUiStatus(Status uiStatus) {
this.uiStatus = uiStatus;
}
public void setValue(int value) {
this.value = value;
}
}

Categories