Jasper – Crosstab + nullable field
Bom dia galera,
Hoje vamos sair um pouco do mundo .Net e vamos entrar um pouquinho no Java, Jasper Reports.
O problema
O problema é, temos um set de dados onde null é diferente de 0. O relatório já está pronto. Quando for null queremos que o campo fique branco e quando for 0 apareça o zero lá. E agora?
Solução: Campo object e um Custom Incrementer Factory baseado no JRIntegerIncrementerFactory
O objetivo é permitir que o campo seja nulo, e através de um text field expression, deixar em branco quando for nulo. Vamos precisar de uma Incrementer Factory para fazer o cálculo do total, pois sendo o field um object:
- Default Incrementer Factory não utilizará o JRIntegerIncrementerFactory;
- JRIntegerIncrementerFactory não pode ser utilizado pois seu construtor é nulo
- JRIntegerIncrementerFactory não pode ser utilizado com superclasse porque é uma classe marcada como “final”.
Sendo assim vamos às alterações
Primeiro, altere o field que deseja como measure para object, conforme imagem
Vamos alterar o campo de texto do detail dentro da crosstab. Vamos marcar a propriedade Blank When null, deixar a expression class como string. (o campo detail é aquele que você deseja que seja a “coluna dinâmica”. Veja imagem:
Nas propriedades do campo utilizado na seção Measures da crosstab podemos especificar um Incrementer Factory. Nesse campo incrementer factory colocamos a classe com o namespace completo para sua classe customizada.
Para desenvolver sua Custom Incrementer Factory, basta criar um novo projeto no eclipse, adicionar a referencia para o .jar do jasper: c:\Program Files\jasperreports-server-cp-4.7.0\apache-tomcat\webapps\jasperserver\WEB-INF\lib\jasperreports-x.y.z.jar
Exemplo de Custom Incrementer
package br.com.marcoslimagon.jasper import net.sf.jasperreports.engine.fill.AbstractValueProvider; import net.sf.jasperreports.engine.fill.JRAbstractExtendedIncrementer; import net.sf.jasperreports.engine.fill.JRAbstractExtendedIncrementerFactory; import net.sf.jasperreports.engine.fill.JRCalculable; import net.sf.jasperreports.engine.fill.JRExtendedIncrementer; import net.sf.jasperreports.engine.type.CalculationEnum; /** * @author marcos.goncalves * @see http://grepcode.com/file_/repo1.maven.org/maven2/net.sf.jasperreports/jasperreports/4.7.1/net/sf/jasperreports/engine/fill/JRIntegerIncrementerFactory.java/?v=source */ public class JRNullableIntegerIncrementerFactory extends JRAbstractExtendedIncrementerFactory { protected static final Integer ZERO = Integer.valueOf(0); public JRNullableIntegerIncrementerFactory() { } @Override public JRExtendedIncrementer getExtendedIncrementer(CalculationEnum calculation) { JRExtendedIncrementer incrementer = null; switch (calculation) { case COUNT : { //incrementer = JRIntegerCountIncrementer.getInstance(); break; } case SUM : { incrementer = JRNullableIntegerSumIncrementer.getInstance(); break; } case AVERAGE : { //incrementer = JRIntegerAverageIncrementer.getInstance(); break; } case LOWEST : case HIGHEST : { //incrementer = JRComparableIncrementerFactory.getInstance().getExtendedIncrementer(calculation); break; } case STANDARD_DEVIATION : { //incrementer = JRIntegerStandardDeviationIncrementer.getInstance(); break; } case VARIANCE : { //incrementer = JRIntegerVarianceIncrementer.getInstance(); break; } case DISTINCT_COUNT : { //incrementer = JRIntegerDistinctCountIncrementer.getInstance(); break; } case SYSTEM : case NOTHING : case FIRST : default : { //incrementer = JRDefaultIncrementerFactory.getInstance().getExtendedIncrementer(calculation); break; } } return incrementer; } } /** * */ final class JRNullableIntegerSumIncrementer extends JRAbstractExtendedIncrementer { /** * */ private static JRNullableIntegerSumIncrementer mainInstance = new JRNullableIntegerSumIncrementer(); /** * */ private JRNullableIntegerSumIncrementer() { } /** * */ public static JRNullableIntegerSumIncrementer getInstance() { return mainInstance; } /** * */ public Object increment( JRCalculable variable, Object expressionValue, AbstractValueProvider valueProvider ) { Number value = (Number)variable.getIncrementedValue(); Number newValue = (Number)expressionValue; if (newValue == null) { if (variable.isInitialized()) { return null; } return value; } if (value == null || variable.isInitialized()) { value = JRNullableIntegerIncrementerFactory.ZERO; } return Integer.valueOf(value.intValue() + newValue.intValue()); } public Object initialValue() { //Starts with null value instead of zero //return JRNullableIntegerIncrementerFactory.ZERO; return null; } }
OBS: Eu só deixei a soma funcionando. Todo o código se baseia na versão 4.7.1 da classe JRIntegerIncrementerFactory do código-fonte do Jasper.
Agora basta gerar o .Jar dessa classe e adicionar no iReport: Ferramentas > opções > classpath > Add jar
Adicione no relatório: JasperServer Report > Add Jar archive
Adicione o .jar gerado na pasta lib do servidor do jasper (não tenho certeza sobre a necessidade desse passo ainda)
Agora na crosstab do seu relatório, na seção Measures, deixe a Measure Class como Object e coloque sua IncrementerFactory (com namespace completo) conforme imagem
Voilà! Quando executar a consulta, se o campo usado como measure/details for null ele exibirá branco, se vier 0 ele exibirá o 0. Ambos null e 0 não farão parte da soma do total.
Abraços! e até a próxima!!
- Asp.Net MVC 4 – Como buscar uma url dentro do seu site
- Certificação Microsoft – Second Shot