如何将POJO属性名映射到.jrxml文件字段名?支持注解吗?
JasperReports本身没有内置的类似@Field的注解来实现POJO属性与jrxml字段名的自定义映射,但你可以通过以下几种方案实现需求:
方案1:自定义注解+自定义BeanPropertyAccessor
这是最灵活的方案,适合需要批量映射或后续扩展的场景。
步骤1:定义自定义映射注解
创建一个运行时注解,用于标记POJO属性对应的jrxml字段名:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface JasperField { String value(); // 对应jrxml中的字段名 }
步骤2:在POJO中使用注解
给需要映射的属性添加注解:
public class Customer { @JasperField("customer_name") private String customerName; // 标准getter/setter public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } }
步骤3:实现自定义属性访问器
实现JasperReports的JRBeanPropertyAccessor接口,重写属性读取逻辑,优先读取注解中的映射名:
import net.sf.jasperreports.engine.JRPropertiesMap; import net.sf.jasperreports.engine.data.JRBeanPropertyAccessor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class AnnotatedBeanPropertyAccessor implements JRBeanPropertyAccessor { @Override public Object getProperty(Object bean, String propertyName) throws Exception { // 先查找带@JasperField注解的字段 Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { JasperField annotation = field.getAnnotation(JasperField.class); if (annotation != null && annotation.value().equals(propertyName)) { // 调用对应getter方法(符合JavaBean规范) String getterName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1); Method getter = bean.getClass().getMethod(getterName); return getter.invoke(bean); } } // 无注解时,降级处理:下划线转驼峰后匹配属性 String camelCaseProp = convertUnderscoreToCamelCase(propertyName); String getterName = "get" + camelCaseProp.substring(0, 1).toUpperCase() + camelCaseProp.substring(1); Method getter = bean.getClass().getMethod(getterName); return getter.invoke(bean); } @Override public void setProperty(Object bean, String propertyName, Object value) throws Exception { // 同理实现属性写入逻辑(如果需要) Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { JasperField annotation = field.getAnnotation(JasperField.class); if (annotation != null && annotation.value().equals(propertyName)) { String setterName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1); Method setter = bean.getClass().getMethod(setterName, field.getType()); setter.invoke(bean, value); return; } } String camelCaseProp = convertUnderscoreToCamelCase(propertyName); Field field = bean.getClass().getDeclaredField(camelCaseProp); String setterName = "set" + camelCaseProp.substring(0, 1).toUpperCase() + camelCaseProp.substring(1); Method setter = bean.getClass().getMethod(setterName, field.getType()); setter.invoke(bean, value); } @Override public boolean hasProperty(Object bean, String propertyName) { try { getProperty(bean, propertyName); return true; } catch (Exception e) { return false; } } @Override public void init(JRPropertiesMap propertiesMap) { // 无需初始化,留空即可 } // 下划线转驼峰工具方法 private String convertUnderscoreToCamelCase(String input) { StringBuilder sb = new StringBuilder(); boolean nextUpper = false; for (char c : input.toCharArray()) { if (c == '_') { nextUpper = true; } else { sb.append(nextUpper ? Character.toUpperCase(c) : c); nextUpper = false; } } return sb.toString(); } }
步骤4:在数据源中配置自定义访问器
创建JRBeanCollectionDataSource时,设置自定义的属性访问器:
import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import java.util.List; // 你的数据列表 List<Customer> customerList = ...; // 创建数据源并绑定自定义访问器 JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(customerList); dataSource.setBeanPropertyAccessor(new AnnotatedBeanPropertyAccessor()); // 后续填充报表逻辑不变 // JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, dataSource);
方案2:POJO转Map中间映射
如果不想写自定义注解和访问器,可以手动将POJO转换为Map,用Map的key对应jrxml中的字段名:
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; // 转换工具方法 public class CustomerMapper { public static Map<String, Object> toReportMap(Customer customer) { Map<String, Object> map = new HashMap<>(); map.put("customer_name", customer.getCustomerName()); // 其他属性按同样方式映射 return map; } } // 生成数据源 List<Map<String, Object>> reportData = customerList.stream() .map(CustomerMapper::toReportMap) .collect(Collectors.toList()); JRDataSource dataSource = new JRMapCollectionDataSource(reportData);
这种方式简单直接,但属性较多时需要编写大量转换代码,扩展性较差。
方案3:jrxml中直接调用getter方法
如果只是个别字段需要映射,可以在jrxml的表达式中直接调用POJO的getter方法,无需额外Java代码:
<textField> <reportElement x="0" y="0" width="100" height="20"/> <textFieldExpression><![CDATA[((your.package.Customer)$P{REPORT_CURRENT_DATA}).getCustomerName()]]></textFieldExpression> </textField>
这种方式适合少量字段的场景,但会让jrxml与POJO的耦合度提高。
内容的提问来源于stack exchange,提问作者Việt Trần Mạnh
相关产品推荐
相关产品推荐

