How to run update sql natively and not calling JPA PreUpdate - mysql

In my model class Account, in the jpa #PreUpdate I am setting the value of the field lastUpdateDate to the current date and time.
The Class model looks like this:
class Account {
private String m_id;
private String m_fullName;
...
private Date m_indexDate;
private Date m_lastUpdateDate;
#Id
public String getId() {
return m_id;
}
#Audited
public String getFullName() {
return m_fullName;
}
public String getIndexDate() {
return m_indexDate;
}
public String getLastUpdateDate() {
return m_indexDate;
}
...
#PreUpdate
void preUpdate() {
setLastUpdateDate(new Date());
}
}
I also have indexDate which is updated after every successfull sync/update with Elasticsearch.
Now, because of #PreUpdate ... lastUpdateDate always gets updated whenever I update the indexDate via JPA save or update... or even via #Query (native = true).
Is there a way to update indexDate (via native sql update) or other way but I will not hit #PreUpdate so that lastUpdateDate will not get updated?
I want to update indexDate without getting lastUpdateDate updated. #PreUpdate is working fine.. I jsut dont want to call this on indexDate update which is triggered only after successful update/sync with elasticsearch.

Related

JDBCTemplate : how to fetch values of MySQL variables

I want to get the value of a MySQL variable (example: max_allowed_packet) via jdbcTemplate. is this possible? if so, how ?
In SQL, I can do SHOW VARIABLES LIKE 'max_allowed_packet'; but how to do this via JDBCTemplate ?
Here is a solution
public List<Variable> findAllVariables() {
List<Variable> result = jdbcTemplate.query("SHOW GLOBAL VARIABLES", new VariableRowMapper());
//about 630 variables
return result;
}
Variable class:
public class Variable {
private String name;
private String value;
//getters and setters
}
VariableRowMapper class:
public class VariableRowMapper implements RowMapper<Variable> {
#Override
public Variable mapRow(ResultSet resultSet, int rowNum) throws SQLException {
String name = resultSet.getString("Variable_Name");
String value = resultSet.getString("Value");
return new Variable(name, value);
}
}
hope it helps.
I was particularly interested in getting the max_allowed_packet variable from the database. This below snippet does the trick.
private int fetchMaxAllowedPacketsFromDB(JdbcTemplate jdbcTemplate) {
final String sql = "SELECT ##GLOBAL.max_allowed_packet";
Integer maxAllowedPacketsFromDB = jdbcTemplate.queryForObject(sql, Integer.class);
log.info("##GLOBAL.max_allowed_packet : {}", maxAllowedPacketsFromDB);
return maxAllowedPacketsFromDB;
}
You can look at #Ali4j 's answer for a more generic/multi-variable requirement.
Or, You can refactor the snippet above to pass in a variable as argument, if you don't need the extra work of RowMappers

How to update only few fields of entity using JPA and Hibernate?

I'm using MySQL DB.
My entity for the table is Account with the following fields:
id(long), balance (double), created_on(Date), currency(Enum).
When I'm doing a PUT request to update the account, I pass in the request body JSON.
I want to update, for example, only the balance, but the other columns' values to be saved.
In that case (I'm not passing the currency type) the balance is updated, but the currency has value NULL. Is that because it's enum?
I've tried using #DynamicUpdate annotation, but still, it doesn't have any change.
#RestController
public class AccountController {
#PutMapping("/accounts/{id}")
public void updateAccount(\#PathVariable long id, #RequestBody AccountDto accountDto) {
accountService.updateAccount(id, accountDto);
}
}
I'm using AccountDto (which I pass in the request body) and I'm calling the accountService
public void updateAccount(long id, AccountDto accountDto) {
Account account = accountRepository.getOne(id);
account.fromDto(accountDto);
this.accountRepository.save(account); }),
which calls the AccountRepository
public void fromDto(AccountDto accountDto) {
this.balance = accountDto.getBalance();
this.currency = accountDto.getCurrency();
}
Here is the AccountDto class:
public class AccountDto {
private long id;
#NotNull #PositiveOrZero
private double balance;
#NotNull #Enumerated(EnumType.STRING)
private Currency currency;
}
You need to perform a select query on Account entity and then update only the desired fields.
(Eg - making assumptions of my own of underlying method being used for accessing DB)
public updateAccount(AccountModel jsonBody) {
Account entity = accountRepository.findById(jsonBody.getAccountId());
entity.setBalance(jsonBody.getBalance());
accountRepository.save(entity);
}
If you get null as currency in the JSON you shouldn't update it:
So fromDto must look like:
public void fromDto(AccountDto accountDto) {
this.balance = accountDto.getBalance();
if (accountDto.getCurrency() != null) {
this.currency = accountDto.getCurrency();
}
}

How handle currents updates in spring-boot hibernate problem? Also need to make app scalable

Project type :- Spring-boot JPA project
Hi,
I have below Rest service which increments a number in database.
#RestController
public class IncrementController {
#Autowired
MyNumberRepository mynumberRepository;
#GetMapping(path="/incrementnumber")
public String incrementNumber(){
Optional<MyNumber> mynumber = mynumberRepository.findById(1);
int i = mynumber.get().getNumber();
System.out.println("value of no is "+i);
i = i+1;
System.out.println("value of no post increment is "+i);
mynumber.get().setNumber(i);
MyNumber entity = new MyNumber();
entity.setId(1);
entity.setNumber(i);
mynumberRepository.save(entity);
return "done";
}
}
Entity is as below :-
#Entity
#Table(name = "my_number")
public class MyNumber {
#Id
private Integer id;
private Integer number;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}
Below is the Repository :-
public interface MyNumberRepository extends JpaRepository<MyNumber, Integer>{
}
The service works well when I call increment number sequentially , but when concurrent threads call the incrementservice then i get non consistent results. How can I handle this situation ?
Also have to deploy the app on multiple places and connecting to same DB. i.e Scalability concern.
Thanks,
Rahul
You must use a pessimistic lock. This will issue a SELECT FOR UPDATE and lock the row for the transaction and it's not possible for another transaction to overwrite the row.
public interface MyNumberRepository extends JpaRepository<MyNumber, Integer> {
#Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<MyNumber> findById(Integer id);
}
And then you have to make your REST method transactional by adding #Transactional
#RestController
public class IncrementController {
#Autowired
MyNumberRepository mynumberRepository;
#Transactional
#GetMapping(path="/incrementnumber")
public String incrementNumber(){
Optional<MyNumber> mynumber = mynumberRepository.findById(1);
int i = mynumber.get().getNumber();
System.out.println("value of no is "+i);
i = i+1;
System.out.println("value of no post increment is "+i);
mynumber.get().setNumber(i);
MyNumber entity = new MyNumber();
entity.setId(1);
entity.setNumber(i);
mynumberRepository.save(entity);
return "done";
}
}
Above solution will work , but i feel you are doing over-engineering for very simple problem.
My recommendation would be to use database sequence.I feel your requirement is quite straight forward.In your service u can simply call getnextvalue on the sequence and then set the value in the Id field.This way u don't have to manage locks also as Database will do that for you.
In oracle particularly sequences are managed in a different transactions . So if ur calling code fails with exception , still the value of sequence will be incremented . This will ensure that multi-threads will not see the same value of the sequence in case of exceptions.
Instead of locking transaction, you could also use an Oracle sequence or MySQL "AUTO_INCREMENT" feature which will prevent any ID being returned twice.
https://community.oracle.com/thread/4156674
Thread safety of MySql's Select Last_Insert_ID

Getting AutoIncrement value after saving in Spring

I'm working with Spring Boot 2 (Hibernate, JPA) and MySQL. I need a unique, auto increment custom reference number, for example 18/10/5 (here, 18 is year, 10 is month, 5 is auto increment field). The strategy I used is:
Create an auto increment field. Get the value of auto increment after saving and joining with yy/MM/.
I removed unwanted annotations for easiness.
Model class
#Entity
#Table(name="account")
public class Account{
#Id
private String id;
#GeneratedValue(strategy=GenerationType.AUTO)
private Long autoIncrement;
String refNo;
//Other fields, Constructors, Getters And Setters
}
Then in the controller, first I save, then I get the id of the saved object and trying to update
Controller class
#RestController
#RequestMapping("/account")
public class AccountController {
#PostMapping("/save")
public ModelAndView saveAccount(#ModelAttribute("account") Account account){
//few codes
accountService.save(account) //Saving
accountService.updateRefNo(account.getId()) //try to update
}
}
Service class
#Service
#Transactional
public class AccountServiceImpl implements AccountService{
#Autowired
AccountRepository accountRepository;
//Few methods
public void updateRefNo(String id) {
Account account=findById(id); // return the object
String year = // getting year
String month = //getting month
account.setRefNo(year+"/"+month+"/"+account.getAutoIncrement());
}
}
Data is saved. When I try to update, account.getAutoIncrement() returns null, but its saved in the database. I tried saveAndFlush() also. Why does this happen? Not committed?
in your updateRefNo I dont see save or update call on Account entity
public void updateRefNo(String id) {
Account account=findById(id); // return the object
String year = // getting year
String month = //getting month
account.setRefNo(year+"/"+month+"/"+account.getAutoIncrement());
// update in DB
accountRepository.save(account); or accountRepository.update(account);
}

How to update preload data in spring when DB record updated

I use Spring and Hibernate for my application and want to load some configuration data from DB when the application launches. But the question is how I can update the preloaded data in case the record in DB is updated (btw, I use mysql for the DB).
I found some solutions said using the trigger in mysql, but I don't know whether the trigger can call some WebService all send some events to tell the application.
Are there some more solutions or good ideas for the case?
Here's the sample code of preloading data:
#Service
public class PreloadService {
#Autowired
private ConfigurationDao configurationDao;
private Map<String, String> preloadConfs = null;
#PostConstruct
public void getConfigurations() {
Map<String, String> results = new HashMap<String, String>();
List<Configuration> confs = configurationDao.findConfigurations(null, null);
for (Configuration c: confs) {
results.put(c.getCode(), c.getDescription());
}
preloadConfs = results;
}
public String getDescription(String code) {
return preloadConfs.get(code);
}
}
and other class should use the class like this
#Autowired
private PreloadService preloadService;
public void foo() {
preloadService.getDescription("code");
}
So the question is how I can update the preloadConfs object if the configuration table was changed?