Search code examples
javaspringspring-bootthymeleaf

How to show each data from table separated by date


I'm curious to find a solution for this but couldn't find anything relatable and useful so far.

Lets say user have two transactions for 18/01/2023

I want to have a heading for example like separate <div> and to pass today date and each transaction from today, and below down for example 15/01/2023 because user also have a transactions on that date.

This is an example, as you can see I have today section because user made a transaction on today date, also for yesterday and then back on January 14, so I want to separate transactions for each that like that. Example

This is Transaction class:

@Entity
@Table(name = "transaction")
public class Transaction {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "transaction_id")
    private Long id;

    @Column(name = "user_id", nullable = false)
    private Long userId;

    @Column(name = "wallet_name", nullable = false)
    private String walletName;

    @NotNull(message = "Please, insert a amount")
    @Min(value = 0, message = "Please, insert a positive amount")
    private Double amount;

    private String note;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Column(name = "date")
    private LocalDate date;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "wallet_id", nullable = false)
    private Wallet wallet;

    @Enumerated(EnumType.STRING)
    @Column(name = "transaction_type", columnDefinition = "ENUM('EXPENSE', 'INCOME')")
    private TransactionType transactionType;

    @Nullable
    @Enumerated(EnumType.STRING)
    @Column(name = "expense_categories", columnDefinition = "ENUM('FOOD_AND_DRINK', 'SHOPPING', 'TRANSPORT', 'HOME'," +
            " 'BILLS_AND_FEES', 'ENTERTAINMENT', 'CAR', 'TRAVEL', 'FAMILY_AND_PERSONAL', 'HEALTHCARE'," +
            " 'EDUCATION', 'GROCERIES', 'GIFTS', 'BEAUTY', 'WORK', 'SPORTS_AND_HOBBIES', 'OTHER')")
    private ExpenseCategories expenseCategories;

    @Nullable
    @Enumerated(EnumType.STRING)
    @Column(name = "income_categories", columnDefinition = "ENUM('SALARY', 'BUSINESS', 'GIFTS', 'EXTRA_INCOME', 'LOAN', 'PARENTAL_LEAVE', 'INSURANCE_PAYOUT', 'OTHER')")
    private IncomeCategories incomeCategories;

I created a new class that will act as group and that is like this:

 class TransactionGroup {
 private LocalDate date;
 private List<Transaction> transactions;
 /* Getters and setters */
 }

And Thymeleaf:

    <div th:each="singleGroup  : ${transactionGroup}">
    <h1 th:text="${singleGroup .date}"></h1>

    <div th:each="singleTrans  : ${singleGroup.transactions}">
        <h2>Amount: <span th:text="${singleTrans .amount}"></span></h2><br>
        <h2>Note: <span th:text="${singleTrans .note}"></span></h2><br>
        <h2>Wallet name: <span th:text="${singleTrans .walletName}"></span></h2><br>
        <h2>Expense Category: <span th:text="${singleTrans .expenseCategories}"></span></h2><br>
        <h2>IncomeCategory: <span th:text="${singleTrans .incomeCategories}"></span></h2>
        <div>
        </div>
    </div>
</div>

And this is controller:

   @GetMapping("/userTransactions/{user_id}")
public String getUserTransactions(@PathVariable("user_id") long user_id, TransactionGroup transactionGroup, Model model) {
    List<Transaction> transactions = transactionRepository.getTransactionsByUserId(user_id);
    //create a TransactionGroup list
    List<TransactionGroup> transactionByDate = new ArrayList<>();
    //create a list that will hold all transactions for a day
    List<Transaction> transOnSingleDate = new ArrayList<>();
    //initialize currDate with the first transaction date
    LocalDate currDate = transactions.get(0).getDate();

    //create a TransactionGroup
    TransactionGroup transGroup = new TransactionGroup();

    //loop through your transactions and populate the wrapper list
    for(Transaction t : transactions){
        //create a new transaction group if the date has changed
        if(!currDate.isEqual(t.getDate())){
            //fill the wrapper list before creating a new list
            transGroup.setDate(currDate);
            transGroup.setTransactions(transOnSingleDate);
            transactionByDate.add(transGroup);
            //create new TransactionGroup and List<Transaction> for a new date
            transGroup = new TransactionGroup();
            transOnSingleDate = new ArrayList<>();
        }

        transOnSingleDate.add( t );
        currDate = t.getDate();
    }
    //add the final list
    transGroup.setDate(currDate);
    transGroup.setTransactions(transOnSingleDate);
    transactionByDate.add(transGroup);
    model.addAttribute("transactionGroup", transactionByDate);
    return "transactions";

}

You can see that I populate here transaction list in TransactionGroup by transactions from entity Transaction:

transactionGroup.setTransactions(transactionService.findDistinctIdByUserId(userId));

And on page I can see transactions, but I cant see a date how I want, I cant even see date, date is not displayed, because I didn't populate a date field on class TransactionGroup with date from Transaction class. How I can get a transaction date from each transaction that is created?

I guess I need to populate it somehow like transactionGroup.setTransactions... but now like transactionGroup.setDate... but Date is not a list type, so there is a problem.

Its obivous that if I try with transactionGroup.setDate(transaction.getDate); returning null

SOUT LOGS:

    transGroup TransactionGroup{date=2023-01-01, transactions=null}
transactionByDate [TransactionGroup{date=2023-03-01, transactions=[Transaction{id=18, userId=1, walletName='Dailyk', amount=4.0, note='Cetvrta transakcija', date=2023-03-01, wallet=com.budgettracker.demo.userProfile.models.Wallet@68e4f813, transactionType=INCOME, expenseCategories=null, incomeCategories=BUSINESS}]}, TransactionGroup{date=2023-02-01, transactions=[Transaction{id=17, userId=1, walletName='Dailyk', amount=3.0, note='Treca transakcija', date=2023-02-01, wallet=com.budgettracker.demo.userProfile.models.Wallet@68e4f813, transactionType=INCOME, expenseCategories=null, incomeCategories=EXTRA_INCOME}]}, TransactionGroup{date=2023-01-01, transactions=[Transaction{id=15, userId=1, walletName='Dailyk', amount=1.0, note='Prva transkacija', date=2023-01-01, wallet=com.budgettracker.demo.userProfile.models.Wallet@68e4f813, transactionType=INCOME, expenseCategories=null, incomeCategories=SALARY}, Transaction{id=16, userId=1, walletName='Dailyk', amount=2.0, note='Druga transkacija', date=2023-01-01, wallet=com.budgettracker.demo.userProfile.models.Wallet@68e4f813, transactionType=INCOME, expenseCategories=null, incomeCategories=GIFTS}]}]

Solution

  • This can be done in many ways. For instance, you can define a wrapper list ( List<List<Transaction>> or List<TransactionGroup>) where the contained list(s) will contain all the transactions on a given day. It would also make sense to create a native query to retrieve the transactions for a particular user sorted by date.

    //your Transaction repository

    @Query( value="select * from transaction where user_id = ?1 order by date desc", nativeQuery=true)
    List<Transaction> getTransactionsByUserId(Integer userId);
    

    The logic in your controller could then look something like

        ....
        //this list holds all the transactions for a particular user
        List<Transaction> transactions = transRepository.getTransactionsByUserId(userId);
    
       //create the wrapper list
       List<List<Transaction>> transactionByDate = new ArrayList<>();
    
       //initialize currDate with the first transaction date
       LocalDate currDate = transactions.get(0).getDate();
    
       //create a list that will hold transactions for a single date
       List<Transaction> transOnSingleDate = new ArrayList<>();
    
        //loop through your transactions and populate the wrapper list
        for(Transaction t : transactions){
            //create a new list of transactions if the date has changed 
            if(!currDate.isEqual(t.getDate()){
                //fill the wrapper list before creating a new list
                transactionByDate.add(transOnSingleDate);
                transOnSingleDate = new ArrayList<>();
             }
            transOnSingleDate.add( t );
            currDate = t.getDate();       
         }
         //add the final list
         
         transactionByDate.add(transOnSingleDate);
         model.addAtrribute("transByDate", transactionByDate);
    

    or using List<TransactionGroup>

       ....
        //this list holds all the transactions for a particular user
        List<Transaction> transactions = transRepository.getTransactionsByUserId(userId);
       //create a TransactionGroup list
       List<TransactionGroup> transactionByDate = new ArrayList<>();
       //create a list that will hold all transactions for a day
        List<Transaction> transOnSingleDate = new ArrayList<>();
       //initialize currDate with the first transaction date
       LocalDate currDate = transactions.get(0).getDate();
    
       //create a TransactionGroup
       TransactionGroup transGroup = new TransactionGroup();
    
        //loop through your transactions and populate the wrapper list
        for(Transaction t : transactions){
            //create a new transaction group if the date has changed 
            if(!currDate.isEqual(t.getDate()){
                //fill the wrapper list before creating a new list
                transGroup.setDate(currDate);
                transGroup.setTransactions(transOnSingleDate);
                transactionByDate.add(transGroup);
                //create new TransactionGroup and List<Transaction> for a new date
                transGroup = new TransactionGroup();
                transOnSingleDate = new ArrayList<>();
             }
            
            transOnSingleDay.add( t );
            currDate = t.getDate();       
         }
         //add the final list
         transGroup.setDate(currDate);
         transGroup.setTransactions(transOnSingleDate);
         transactionByDate.add(transGroup);
         model.addAtrribute("transactionGroup", transactionByDate);
    

    Hope this helps.

    UPDATE:

    The controller part is now ok. The List<TransactionGroup> object contains 3 transaction groups, one for each date. The thymeleaf template is wrong. It should be something like

    <div th:each="singleGroup : ${transactionGroup}">  
    <div th:each="singleTrans : ${singleGroup.transactions}"> 
    <h2>Amount: <span th:text="${singleTrans.amount}"></span></h2><br>
    <h2>Note: <span th:text="${singleTrans.note}"></span></h2><br>
    <h2>Wallet name: <span th:text="${singleTrans.walletName}"></span></h2><br>
    <h2>Expense Category: <span th:text="${singleTrans.expenseCategories}"></span></h2><br>
    <h2>IncomeCategory: <span th:text="${singleTrans.incomeCategories}"></span></h2>
    <h2>IncomeCategory: <span th:text="${singleTrans.date}"></span></h2>
    <div>
    </div>