You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin非缓冲旋转网格实现问题及修复方案:学生月度积分按日期-学生维度展示

Fixing Non-Buffered Pivot Grid Issues in Vaadin

Let's tackle the two main issues you're facing with your Vaadin pivot grid implementation for the SchoolBook entity: duplicate rows per date, missing dates, and we'll also add the required monthly sum row to complete your grid.

Problem Breakdown

First, let's recap the core problems from your initial code:

  1. Duplicate rows per date: Your original getDataForSpecificMonth method created a new SyntheticRow for every single SchoolBook entry on the same date. This split student points across multiple rows instead of grouping all students' points into one row per date.
  2. Missing dates: Dates with no SchoolBook records were completely omitted from the grid, leaving gaps in your date sequence.

Fixed Implementation

Step 1: Correct the getDataForSpecificMonth Method

Your updated method already fixes both core issues, but let's break down why it works and refine it slightly for clarity:

public List<SyntheticRow> getDataForSpecificMonth(PageRequest of, int month, int year) {
    List<SyntheticRow> syntheticRows = new ArrayList<>();
    int numberOfDays = getNumberOfDaysInMonth(month, year);
    
    for (int day = 1; day <= numberOfDays; day++) {
        LocalDate currentDate = LocalDate.of(year, month, day);
        List<SchoolBook> schoolBooks = repository.findByDay(currentDate);
        
        // Create ONE row per date, even if there are no entries
        SyntheticRow syntheticRow = new SyntheticRow();
        syntheticRow.setDate(currentDate);
        Map<Long, String> studentPointsMap = new HashMap<>();
        
        // Populate the map with all student points for this date
        for (SchoolBook book : schoolBooks) {
            studentPointsMap.put(book.getStudentId(), book.getPoints());
        }
        
        syntheticRow.setMap(studentPointsMap);
        syntheticRows.add(syntheticRow);
    }
    
    return syntheticRows;
}

Key improvements here:

  • We create one SyntheticRow per date, regardless of whether there are SchoolBook entries for that day. This ensures every date in the month appears in the grid, no gaps.
  • All student points for the same date are added to a single map in the row, so all students' points show up in the correct columns for that date row.

Step 2: Clean Up Empty Column Values

Right now, if a student has no entry for a date, the column will display null. To make the grid look cleaner, add a default value (like "-") in your column generator:

for (Student student : students) {
    Long studentId = student.getStudentId();
    grid.addColumn(syntheticRow -> {
        String points = syntheticRow.getMap().get(studentId);
        return points != null ? points : "-"; // Show "-" when no entry exists
    }).setHeader(student.getStudentName() + " (" + studentId + ")");
}

Step 3: Add the Monthly Sum Row

To implement the final sum row, we need to calculate total points (converting letter grades to numeric values) for each student in the selected month. Let's add this functionality to your service, then append the sum row to the grid.

Service Method to Calculate Monthly Sums

public Map<Long, Integer> calculateMonthlyStudentSums(int month, int year, Long classId) {
    Map<Long, Integer> studentTotalSums = new HashMap<>();
    YearMonth targetMonth = YearMonth.of(year, month);
    
    // Fetch all SchoolBook entries for the target month in one query (better performance)
    List<SchoolBook> monthlyEntries = repository.findByDayBetween(
        targetMonth.atDay(1),
        targetMonth.atEndOfMonth()
    );
    
    // Convert letters to numeric values and sum for each student
    for (SchoolBook book : monthlyEntries) {
        Long studentId = book.getStudentId();
        int pointsValue = convertPointsToNumeric(book.getPoints());
        studentTotalSums.put(studentId, studentTotalSums.getOrDefault(studentId, 0) + pointsValue);
    }
    
    // Initialize sum to 0 for students with no entries in the month
    List<Student> allStudents = userService.getAllStudentsFromClass(classId);
    for (Student student : allStudents) {
        studentTotalSums.putIfAbsent(student.getStudentId(), 0);
    }
    
    return studentTotalSums;
}

// Helper method to convert letter grades to their numeric equivalents
private int convertPointsToNumeric(String points) {
    return switch (points.toUpperCase()) {
        case "A" -> 10;
        case "B" -> 9;
        case "C" -> 8;
        case "D" -> 7;
        case "E" -> 6;
        case "F" -> 5;
        default -> Integer.parseInt(points); // Handle numeric point strings
    };
}

Append the Sum Row to Your Grid

Modify your grid setup code to add the sum row. We'll create a special SyntheticRow for totals and update the date column to show "Total" instead of a date:

// Load regular date rows
List<SyntheticRow> dateRows = syntheticRowService.getDataForSpecificMonth(
    PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)),
    9, 2022
);

// Calculate monthly sums and create the sum row
Map<Long, Integer> studentSums = syntheticRowService.calculateMonthlyStudentSums(9, 2022, classId);
SyntheticRow sumRow = new SyntheticRow();
sumRow.setDate(null); // Mark as the sum row
Map<Long, String> sumMap = new HashMap<>();
studentSums.forEach((studentId, total) -> sumMap.put(studentId, String.valueOf(total)));
sumRow.setMap(sumMap);

// Combine date rows and sum row, then set to grid
List<SyntheticRow> allRows = new ArrayList<>(dateRows);
allRows.add(sumRow);
grid.setItems(allRows);

// Update date column to show "Total" for the sum row
grid.addColumn(syntheticRow -> {
    LocalDate date = syntheticRow.getDate();
    return date != null ? date.toString() : "Total";
}).setHeader("Date");

// Keep the student column logic with default values
for (Student student : students) {
    Long studentId = student.getStudentId();
    grid.addColumn(syntheticRow -> {
        String value = syntheticRow.getMap().get(studentId);
        return value != null ? value : "-";
    }).setHeader(student.getStudentName() + " (" + studentId + ")");
}

Final Notes

  • Pagination: Since we're showing an entire month's data (max 31 rows plus 1 sum row), consider disabling pagination for this grid—it's a small dataset that doesn't need it, and pagination would split the sum row from the rest of the data.
  • Performance: Fetching all monthly entries in one query (instead of per day) reduces database calls, which is more efficient for larger datasets.

内容的提问来源于stack exchange,提问作者Andromeda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 13:57:29