how to handle exceptions when using Room with LiveData - android-room

when using android-room with LiveData together, it sometimes throw Exeptions such as SQLiteCantOpenDatabaseException & SQLiteFullException, but I can't find a chance to catch the excpetions as the LiveData object was generated automaticlly by Room. how can i handle exceptions when using Room with LiveData?
below is my dao:
#Dao
public interface Dao {
#Insert(onConflict = IGNORE)
long add(TraceEntity host);
#Query("select * from trace where _id = :id")
Entity findById(long id);
#Delete
int del(Entity host);
#Query("select * from trace")
LiveData<List<Entity>> queryAll();
#Query("SELECT COUNT(_id) FROM trace")
LiveData<Integer> getCount();
}

Related

Springboot Reactive findByusername return "monomap" by r2dbc

I am writing the reactive springboot with mysql and r2dbc.When i writing the query with findByUsername, it just return a String "monotype" instead of an object.
Code:
#Override
public Mono<UserDetails> findByUsername(String username) {
log.info("get user");
System.out.println(userRespository.findByUsername(username)); //print "monoNext" in the console
Mono<UserDetails> ans= userRespository.findByUsername(username).switchIfEmpty(Mono.error(new RuntimeException())).map(
SecurityUser::new
);
return ans;
My respository:
#Repository
public interface UserRespository extends R2dbcRepository<User,Integer> {
#Query("SELECT * FROM user_info WHERE username = :username ;")
Mono<User> findByUsername(String username);
}
ANyone have idea for it?
It is because you actually print Mono.toString() because repository returns Mono
To print User when it is found you should put a callback to your reactive chain, using, for example, doOnNext() operator
return userRespository.findByUsername(username)
// put this callback means "when user is found, print whatever you want here"
.doOnNext(user -> System.out.println(user))
.switchIfEmpty(Mono.error(new RuntimeException()))
.map(SecurityUser::new);

Spring not picking updated #Query value Using AOP

JPA: Method
#Repository
public interface FloorRepository extends JpaRepository<TnFloor, Integer> {
#Query("select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1")
public List<TnFloor> findByBuildingIds(List<Integer> buildingIds);
}
#Before("dataRolesPointCuts()")
public void beforeMethods(JoinPoint joinPoint) {
log.debug(" Before Advice Called " + joinPoint.toShortString());
String classArray[]=joinPoint.getTarget().getClass().getGenericInterfaces()[0].getTypeName().split("\\.");
String className = classArray[classArray.length-1];
String methodName = joinPoint.getSignature().getName();
String securedMethodName = className + "_" + methodName;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication!=null)
{
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
String loggedINUserName = userDetails.getUsername();
Map<String, Map<String, String>> userRoleMap = usernameRoleMap.get(loggedINUserName);
TnMethodSecurityModel methodSecurity = methodSecurityMap.get(securedMethodName); // Replace with Map
if(methodSecurity!=null && methodSecurity.getTnMethodSecurityFilters()!=null && methodSecurity.getTnMethodSecurityFilters().size()>0)
{
Class<?> clazz =((Class<?>) joinPoint.getTarget().getClass().getGenericInterfaces()[0]);
try {
Method[] methods = clazz.getMethods();
Method method=null;
for(Method meth: methods )
{
if(meth.getName().equals(methodName))
{
method=meth;
break;
}
}
if(method == null)
return;
Query secParam = method.getAnnotation(Query.class);
String query=secParam.value();
String securityPredicate=Util.getSecuirtyPredicate(methodSecurity, userRoleMap);
try {
System.out.println("old MethodAnnotation = " + secParam.value());
Util. changeAnnotationValue(secParam, "value", query+" "+securityPredicate);
System.out.println("Asspect Query :: "+query);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("modified MethodAnnotation = " + secParam.value());
} catch ( SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
log.info("Executing with argument: {}", className + " " + methodName);
}
#SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key,newValue);
return oldValue;
}
Output After Before Advice execution :
old MethodAnnotation = select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1
modified MethodAnnotation = select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1 (tnFloor.tnBuilding.buildingId IN (0,1,6))
But Final JPA is executing query before modification OUTPUT :.
select tnbuilding0_.building_id as building1_17_0_, tnbuilding0_.description as descript2_17_0_, tnbuilding0_.name as name3_17_0_, tnbuilding0_.site_id as site_id4_17_0_, tnsite1_.site_id as site_id1_65_1_, tnsite1_.description as descript2_65_1_, tnsite1_.email as email3_65_1_, tnsite1_.name as name4_65_1_, tnsite1_.url as url5_65_1_ from tn_building tnbuilding0_ inner join tn_site tnsite1_ on tnbuilding0_.site_id=tnsite1_.site_id where tnbuilding0_.building_id=?
Sorry for lecturing you, but... Annotation values are constants. You cannot change them. Your hacky way of trying to change their in-memory representations is maybe a nifty exercise, but bad design. If your application or aspect really rely on something like this, as a developer you should feel an instant urge to refactor instead.
As for why it is not working as you dreamed it up: You might expect that Spring reads annotations every time before executing an annotated method. But usually such frameworks scan annotations while wiring an application.
Simply do not use hard-coded constants (like in annotations) for things you might want to make more dynamic, be it by using AOP or by other means. If you do want to keep the query annotation but use AOP, I think you should try to hook into another component where the query is being parsed or sent to the database and modify it there.

Schedule a function in spring boot with #Scheduled annotation

I would like to execute the following method at the date and time speified on my Angular form
-Here is the input:
<input required [(ngModel)]="emailNotification.sendingDate" class="form-control" type="datetime-local" name="sendingDate" id="time">
The sending emails method(from the controller)
#PostMapping(value="/getdetails")
public #ResponseBody void sendMail(#RequestBody EmailNotification details) throws Exception {
try {
JavaMailSenderImpl jms = (JavaMailSenderImpl) sender;
MimeMessage message = jms.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, StandardCharsets.UTF_8.name());
helper.setFrom("smsender4#gmail.com");
List<String> recipients = fileRepo.findWantedEmails(details.getDaysNum());
String[] to = recipients.stream().toArray(String[]::new);
helper.setTo(to);
helper.setText(details.getMessage(),true);
helper.setSubject("Test Mail");
details.setRecipients(to);
sender.send(message);
enr.save(new EmailNotification(details.getId(), "Test mail", details.getMessage(), details.getDaysNum(), details.getRecipients(), details.getSendingDate()));
} catch (MessagingException e) {
throw new RuntimeException("fail to send emails: " + e.getMessage());
}
EmailNotification.class
public class EmailNotification {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String subject;
private String message;
private int daysNum;
private String[] recipients;
/*
* #Temporal(TemporalType.TIMESTAMP)
*/
//"yyyy-MM-dd'T'HH:mm:ss.SSSX"
#Column(name = "sending_date")
#Basic(fetch = FetchType.EAGER)
private LocalDateTime sendingDate;
public EmailNotification(long id, String subject,String message, int daysNum, String[] recipients, LocalDateTime sendingDate) {
super();
this.sendingDate = sendingDate;
this.daysNum = daysNum;
this.id = id;
this.message = message;
this.subject = subject;
}
I don't know how to proceed, IĆ¹ll be so gratefull if someone helped
Now I think I understand what you are trying to achieve, so check if this works for you.
Let's say you want to send an email when in the given table (I will name it PAYMENT), there's 5 days left for the deadline. Let's say this table has an equivalent entity class named Payment, you could do something like this:
Asumming the Payment entity has it's own CrudRepository extention, which you are using to access the dabatase, you would make a query to find the payments close to the deadline. This of course will depend on how you are accessing your database. The next example query probably won't work (because as far as I know, JPA doesn't support DATEDIFF), but will work as base for what you are trying to achieve
#Repository
public interface PaymentRepository extends CrudRepository<Payment, Integer> {
/* You need to find every payment that's 5 days from the deadline */
#Query("SELECT p FROM PAYMENT p WHERE DATEDIFF(day, CURRENT_TIMESTAMP, p.deadline) <= 5")
public List<PaymentRepository> findPaymentsCloseToDeadline();
}
Make a scheduled task class that sends the email for every payment close to the deadline. Instead of passing parameters to the method, use the database access you already have to fill your email. You should not depend of information received by the front (like Angular), because it's easily manipulable. Use the information you know, the information stored in the database. In this example the tasks starts a 10:00 hours:
#Configuration
#EnableScheduling
public class EmailScheduler {
#Autowired
private PaymentRepository paymentRepository;
#Scheduled(cron = "0 0 10 * * ?")
public void sendEmails() {
JavaMailSenderImpl jms = (JavaMailSenderImpl) sender;
MimeMessage message = jms.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, StandardCharsets.UTF_8.name());
// Here you are accessing the payments close to the deadline
List<Payment> payments = paymentRepository.findPaymentsCloseToDeadline();
/* Send an email for each payment. Use the database info you already have to fill the information */
for (Payment payment : payments) {
try {
helper.setFrom("smsender4#gmail.com");
// Fill the recipients with the info you need
String[] recipients = {payment.getUserEmail(), "other#email.com"};
helper.setTo(recipients);
String msg = "Here would go the message";
helper.setText(msg, true);
helper.setSubject("Test Mail");
sender.send(message);
// You would still need to calculate the ID and the days if you require them
enr.save(new EmailNotification(id, "Test mail", msg, days,
recipients, "today's date in LocalDate"));
} catch (MessagingException e) {
throw new RuntimeException("fail to send emails: " + e.getMessage());
}
}
}
}
This is only a guideline because there's still some things I don't know about your code, but I hope it's clear enough so it helps you. Good luck.

Getting a fixed number of records using Spring Data JPA

I am trying to create a Spring Boot application, where I need to fetch the records from the database and make a call to the REST API for each record fetched from the database. But instead of fetching all the records at once I want to retrieve in batch sizes, say 10, make the rest call for them and then fetch another 10 and do the same, until last record. I am using spring-data-jpa. How can I achieve that?
P.S.: Its a multi-threaded call and DB is the Amazon DynamoDB
My code till now:
Controller:
//Multi Threaded Call to rest
#GetMapping("/myApp-multithread")
public String getQuoteOnSepThread() throws InterruptedException, ExecutionException {
System.out.println("#################################################Multi Threaded Post Call######################");
ExecutorService executor= Executors.newFixedThreadPool(10);
List<Future<String>> myFutureList= new ArrayList<Future<String>>();
long startTime=System.currentTimeMillis()/1000;
***//Here instead of fetching and calling Mycallable for each one, I want
// to do it in batches of 10***
Iterable<Customer> customerIterable=repo.findAll();
List<Customer> customers=new ArrayList<Customer>();
customerIterable.forEach(customers::add);
for(Customer c:customers) {
MyCallable myCallable= new MyCallable(restTemplate, c);
Future<String> future= executor.submit(myCallable);
myFutureList.add(future);
}
for(Future<String> fut:myFutureList) {
fut.get();
}
executor.shutdown();
long timeElapsed= (System.currentTimeMillis()/1000)-startTime;
System.out.println("->>>>>>>>>>>>>>>Time Elapsed In Multi Threaded Post Call<<<<<<<<<<<<<<<-"+timeElapsed);
return "Success";
}
My Callable:
#Scope("prototype")
#Component
public class MyCallable implements Callable<String>{
private RestTemplate restTemplate;
private Customer c;
public MyCallable(RestTemplate rt, Customer cust) {
this.restTemplate = rt;
this.c = cust;
}
#Override
public String call() throws Exception {
System.out.println("Customer no"+ c.getId() +"On thread Number"+Thread.currentThread().getId());
restTemplate.postForObject("http://localhost:3000/save", c, String.class);
return "Done";
}
}
How can I achieve that?
Spring Data JPA offers Page and Slice to handle this case (see PagingAndSortingRepository and Pageable)
public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
You can create Pageable request as:
Pageable firstPageWithTwoElements = PageRequest.of(0, 2);
and pass it to your custom repository (which should extend PagingAndSortingRepository):
Page<T> pageResult = customRepository.findAll(firstPageWithTwoElements);

Spring Transactional method not working properly (not saving db)

I have spent day after day trying to find a solution for my problem with Transactional methods. The logic is like this:
Controller receive request, call queueService, put it in a PriorityBlockingQueue and another thread process the data (find cards, update status,assign to current game, return data)
Controller:
#RequestMapping("/queue")
public DeferredResult<List<Card>> queueRequest(#Params...){
queueService.put(result, size, terminal, time)
result.onCompletion(() -> assignmentService.assignCards(result, game,room, cliente));
}
QueueService:
#Service
public class QueueService {
private BlockingQueue<RequestQueue> queue = new PriorityBlockingQueue<>();
#Autowired
GameRepository gameRepository;
#Autowired
TerminalRepository terminalRepository;
#Autowired
RoomRpository roomRepository;
private long requestId = 0;
public void put(DeferredResult<List<Card>> result, int size, String client, LocalDateTime time_order){
requestId++;
--ommited code(find Entity: game, terminal, room)
try {
RequestQueue request= new RequestCola(requestId, size, terminal,time_order, result);
queue.put(request);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CardService:
#Transactional
public class CardService {
#Autowired
EntityManager em;
#Autowired
CardRepository cardRepository;
#Autowired
AsignService asignacionService;
public List<Cards> processRequest(int size, BigDecimal value)
{
List<Card> carton_query = em.createNativeQuery("{call cards_available(?,?,?)}",
Card.class)
.setParameter(1, false)
.setParameter(2, value)
.setParameter(3, size).getResultList();
List<String> ids = new ArrayList<String>();
carton_query.forEach(action -> ids.add(action.getId_card()));
String update_query = "UPDATE card SET available=true WHERE id_card IN :ids";
em.createNativeQuery(update_query).setParameter("ids", ids).executeUpdate();
return card_query;
}
QueueExecutor (Consumer)
#Component
public class QueueExecute {
#Autowired
QueueService queueRequest;
#Autowired
AsignService asignService;
#Autowired
CardService cardService;
#PostConstruct
public void init(){
new Thread(this::execute).start();
}
private void execute(){
while (true){
try {
RequestQueue request;
request = queueRequest.take();
if(request != null) {
List<Card> cards = cardService.processRequest(request.getSize(), new BigDecimal("1.0"));
request.getCards().setResult((ArrayList<Card>) cards);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
AssignService:
#Transactional
public void assignCards(DeferredResult<List<Card>> cards, Game game, Room room, Terminal terminal)
{
game = em.merge(game);
room = em.merge(room);
terminal = em.merge(terminal);
Order order = new Order();
LocalDateTime datetime = LocalDateTime.now();
BigDecimal total = new BigDecimal("0.0");
order.setTime(datetime)
order.setRoom(room);
order.setGame(game);
order.setId_terminal(terminal);
for(Card card: (List<Card>)cards.getResult()) {
card= em.merge(card)
--> System.out.println("CARD STATUS" + card.getStatus());
// This shows the OLD value of the Card (not updated)
card.setOrder(order);
order.getOrder().add(card);
}
game.setOrder(order);
//gameRepository.save(game)
}
With this code, it does not save new Card status on DB but Game, Terminal and Room saves ok on DB (more or less...). If I remove the assignService, CardService saves the new status on DB correctly.
I have tried to flush manually, save with repo and so on... but the result is almost the same. Could anybody help me?
I think I found a solution (probably not the optimum), but it's more related to the logic of my program.
One of the main problems was the update of Card status property, because it was not reflected on the entity object. When the assignOrder method is called it received the old Card value because it's not possible to share information within Threads/Transactions (as far I know). This is normal within transactions because em.executeUpdate() only commits database, so if I want to get the updated entity I need to refresh it with em.refresh(Entity), but this caused performance to go down.
At the end I changed the logic: first create Orders (transactional) and then assign cards to the orders (transactional). This way works correctly.

Resources