[JAVA] Sortie d'Excel avec des formules avec XlsMapper

Déclencheur

"Je veux sortir Excel en Java, mais je ne veux pas le spécifier par numéro de cellule comme Apache POI", "Je veux une bibliothèque de niveau légèrement supérieur", j'ai trouvé une bibliothèque appelée XlsMapper, alors je l'ai essayé C'était. Veuillez signaler toute erreur ou conseil de mise en œuvre.

Qu'est-ce que XlsMapper?

Une bibliothèque Java qui mappe Excel à Java. Il y avait autrefois une célèbre bibliothèque Java appelée XLSBeans qui a été transformée en livre, mais son développement semble s'être arrêté. Sur la base de cette version 1.1, il semble que les individus ajoutent progressivement des fonctions comme un autre projet XlsMapper. (En fait, j'ai utilisé XLS Beans pendant un certain temps.)

Qu'est-ce qui est plus pratique que POI?

Lors de l'accès à Excel en utilisant POI, pour la liste extraite de la base de données (si vous connaissez les informations), Enregistrer 1. Définissez et mettez en forme l'élément 1 dans la cellule de la colonne Mth du Nième enregistrement dans Excel. Enregistrer 1. Définissez et formatez l'élément 2 dans la cellule de la colonne M + 1ère du Nième enregistrement d'Excel. : Enregistrer 2. Définissez et mettez en forme l'élément 1 dans la cellule de la colonne Mth de N + 1er enregistrement dans Excel. :

Si vous devez implémenter un accès comme celui-ci, ou si la structure de la colonne change, vous devrez décaler les valeurs N et M en conséquence. Avec cette bibliothèque, vous pouvez mapper les valeurs Excel aux POJO et écrire les POJO mappés vers Excel, tout comme un mappeur OR, réduisant ainsi les descriptions de traitement inutiles. De plus, il n'est pas nécessaire de se soucier d'implémenter le format, etc. car les paramètres tels que la copie de l'enregistrement précédent peuvent être définis avec des annotations.

Site officiel

Environnement éprouvé

la mise en oeuvre

Maven Ajoutez ce qui suit à pom. * Omis sauf pour les éléments liés à Excel.

pom.xml


<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>4.0.1</version>
</dependency>
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>4.0.1</version>
</dependency>
<dependency>
	<groupId>com.github.mygreen</groupId>
	<artifactId>xlsmapper</artifactId>
	<version>2.0</version>
</dependency>

Fichier de papeterie

Préparez un livre avec le tableau suivant. Le but est de fermer la table avec une ligne réglée.

無題.png

SpringBootApplication

DemoService.java


@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//Renvoyez simplement l'argument sous forme de carte
		CommandLineParamsMap params = new CommandLineParamsMap(args);
		try (ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args)) {
			DemoService app = ctx.getBean(DemoService.class);
			app.execute(params);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

Service

DemoService.java


@Service
@Slf4j
public class DemoService {
	private static final String TEMPLATE_FILE_PATH = "template\\Liste d'utilisateur_%s année%s mois%création du jour.xlsx";
	private static final String OUTPUT_FILE_PATH = "C:\\test\\Liste d'utilisateur_%s année%s mois%création du jour.xlsx";

	public String execute(CommandLineParamsMap params) {
		log.info("DemoService START");
		LocalDate outPutDate = getOutPutDate(params.getValue("date"));
		log.info("date:" + outPutDate);

		String outputPath = makeTargetPath(outPutDate);

		//Obtenir des données de table
		List<UsingListRecord> target = getData();

		//Définissez chaque information dans la feuille
		UsingListSheet sheet = new UsingListSheet();
		sheet.setOutPutDate("Date de sortie:" + outPutDate);
		sheet.setRecords(target);
		//Ajouter la ligne totale de formule à la dernière ligne
		sheet.addSummaryRecord();

		XlsMapper xlsMapper = new XlsMapper();
		try {
			//Écrivez ceci (aucune boucle requise)!
			xlsMapper.save(
					new FileInputStream(TEMPLATE_FILE_PATH), //Fichier Excel du modèle
					new FileOutputStream(outputPath), //Fichier Excel à écrire
					sheet //Données créées
			);
		} catch (XlsMapperException | IOException e) {
			throw new RuntimeException(e);
		}

		//Lisez le tableau écrit et essayez de sortir
		List<UsingListRecord> records = read(outputPath);
		records.forEach(r->log.info(r.toString()));

		log.info("DemoService END");
		return "###########success###########";
	}

	/**
	 *aaaammjj → objet LocalDate
	 * @param value
	 * @return
	 */
	private LocalDate getOutPutDate(String value) {
		int year = Integer.parseInt(value.substring(0, 4));
		int month = Integer.parseInt(value.substring(4, 6));
		int day = Integer.parseInt(value.substring(6, 8));
		return LocalDate.of(year, month, day);
	}

	//L'acquisition des données
	private List<UsingListRecord> getData() {
		//En fait, apportez-le de DB
		List<UsingListRecord> list = new ArrayList<>();
		list.add(getSample("Anakin Skywalker", "Tatween", "Humain", 100, "Asoka Tano", 1));
		list.add(getSample("Padme Amidara", "Naboo", "Humain", 100, null, 2));
		list.add(getSample("Luke Skywalker", "Police Masa", "Humain", 100, "Caire Ren", 3));
		list.add(getSample("Caire Ren", "Chandrila", "Humain", 100, null, 4));
		list.add(getSample("Asoka Tano", "Siri", "Togruta", 100, null, 5));
		list.add(getSample("Darth Mall", "Dasomie", "Zabrak", null, "Opres sauvages", 6));
		list.add(getSample("Yoda", null, "Yodaの種族", 800, "Luke Skywalker", 7));
		return list;
	}

	//Renvoie un exemple d'enregistrement approprié
	private UsingListRecord getSample(String name, String homeTown, String species, Integer ageAvg, String apprentice,
			int i) {
		LocalDate d1 = LocalDate.of(2019, 3, 1).plusDays(i);
		Date startDate = Date.from(d1.atStartOfDay(ZoneId.systemDefault()).toInstant());

		UsingListRecord record = new UsingListRecord();
		record.setUserName(name);
		record.setPrice(new BigDecimal(120000 + i));
		record.setTax(0.08d + i);
		record.setUsingStartDate(startDate);
		record.setHomeTown(homeTown);
		record.setSpecies(species);
		record.setAgeAvg(ageAvg);
		record.setApprentice(apprentice);
		return record;
	}

	private List<UsingListRecord> read(String targetPath) {
		XlsMapper xlsMapper = new XlsMapper();
		UsingListSheet sheet = null;
		try {
			sheet = xlsMapper.load(
					new FileInputStream(targetPath), //Fichier Excel à lire
					UsingListSheet.class //Classe annotée.
			);
		} catch (XlsMapperException | IOException e) {
			throw new RuntimeException(e);
		}

		return sheet.getRecords();
	}

	private String makeTargetPath(LocalDate batchDate) {
		return String.format(OUTPUT_FILE_PATH, 
				batchDate.getYear(),
				batchDate.getMonthValue(),
				batchDate.getDayOfMonth()
				);
	}
}

Sheet Une classe qui représente une feuille. Donnez @XlsSheet (nom = "nom de la feuille").

UsingListSheet.java


@Slf4j
@Data
@XlsSheet(name = "Liste d'utilisateur")
public class UsingListSheet {

	private String outPutDate;

	@XlsHorizontalRecords(tableLabel = "Liste d'utilisateur", bottom = 3)
	@XlsRecordOption(overOperation = OverOperation.Copy)
	private List<UsingListRecord> records;

	public void addSummaryRecord() {
		if (records == null) {
			this.records = new ArrayList<>();
		}

		UsingListRecord record = new UsingListRecord();
		//Passez votre propre instance
		record.setParent(this);

		record.setUserName("total");
		records.add(record);
	}

	/**
	 *Après avoir écrit le tableau, essayez de régler la date de sortie avec POI.
	 * @param sheet
	 */
	@XlsPostSave
	public void aa(final Sheet sheet) {
		Cell cell = POIUtils.getCell(sheet, 7, 0);
		CellStyle style=cell.getCellStyle();
		cell.setCellValue(outPutDate);
		cell.setCellStyle(style);
	}
}

@XlsHorizontalRecords Définissez-le pour un type de tableau avec un en-tête ci-dessus, tel que l'Excel ci-dessous. Spécifiez le titre de la table avec tableLabel. Le bas spécifie la distance entre la table réelle et le tableLabel. S'il existe un en-tête de tableau directement sous tableLabel, il n'est pas nécessaire de le spécifier.

@XlsRecordOption(overOperation = OverOperation.Copy) Spécifiez ce qu'il faut faire lorsqu'il y a plus de lignes de données que le nombre de lignes spécifié dans le modèle (déterminé par la bordure). Dans le cas d'OverOperation.Copy, le format de la ligne un niveau supérieur est copié et une ligne est ajoutée. OverOperation.Insert corrompt le classeur. (Voir l'environnement que j'ai essayé ci-dessus pour la raison.)

@XlsPostSave La méthode avec ceci sera exécutée automatiquement après l'écriture du fichier. Il peut également être assigné à la méthode de la classe Record, et l'ordre est @XlsPostSave of Sheet → @XlsPostSave of Record. Il y a beaucoup d'autres choses telles que @XlsPreSave, alors voyez ci-dessous. 7. Gestion des événements du cycle de vie

Record Une classe qui représente un enregistrement de la table à placer sur la feuille Excel.

UsingListRecord.java


@Data
public class UsingListRecord {
	//Informations de localisation mappées
	private Map<String, CellPosition> positions;
	//Informations sur le bean parent
	private UsingListSheet parent;

	@XlsColumn(columnName = "Utilisateur")
	private String userName;
	@XlsColumn(columnName = "Frais")
	@XlsFormula(methodName = "getSumFormula", primary = false)
	private BigDecimal price;
	@XlsColumn(columnName = "taux de taxe de vente")
	private Double tax;
	@XlsColumn(columnName = "Date de début d'utilisation")
	@XlsDateTimeConverter(excelPattern = "yyyy/m/d")
	private Date usingStartDate;
	@XlsColumn(columnName = "Lieu de naissance")
	@XlsDefaultValue(value="--", cases=ProcessCase.Save)
	private String homeTown;
	@XlsColumn(columnName = "Course")
	private String species;
	@XlsColumn(columnName = "Vie moyenne")
	private Integer ageAvg;
	@XlsColumn(columnName = "Disciple")
	@XlsDefaultValue(value="--", cases=ProcessCase.Save)
	private String apprentice;

	//Assemblez la formule totale
	public String getSumFormula(Point point) {

		//Formule de sortie uniquement lorsque la ville natale est totale
		if (!userName.equals("total")) {
			return null;
		}

		//Taille de l'enregistrement (valeur recherchant les totaux dans la ligne d'enregistrement)
		final int dataSize = parent.getRecords().size() - 1;

		//Nom de colonne
		final String colAlpha = CellReference.convertNumToColString(point.x);

		//Début de la valeur totale/Numéro de ligne de fin
		final int startRowNumber = point.y - dataSize + 1;
		final int endRowNumber = point.y;

		return String.format("SUM(%s%d:%s%d)", colAlpha, startRowNumber, colAlpha, endRowNumber);

	}
}

@XlsFormula En spécifiant primary = false, s'il y a une valeur dans le champ correspondant, elle sera prioritaire. Si c'est vrai, la formule a toujours la priorité.

@XlsDefaultValue Définissez la valeur par défaut de NULL avec value. Si cases = ProcessCase.Save est spécifié, la valeur par défaut sera définie uniquement pour l'écriture.

Courir

Entrez "date = 20190312" comme argument dans la configuration d'exécution de l'application Springboot, appliquez et exécutez.

Résultat d'exécution

Sortie Excel

Si vous regardez la fenêtre de formule, la formule est également sortie correctement.

無題2.png

Journal

Journal lors de la lecture du tableau après l'écriture. L'acquisition se fait correctement.

Journal (extrait)


com.example.demo.service.DemoService : date:2019-03-12
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A5, price=B5, tax=C5, usingStartDate=D5, homeTown=E5, species=F5, ageAvg=G5, apprentice=H5}, parent=null, userName=Anakin Skywalker, price=120001, tax=1.08, usingStartDate=Sat Mar 02 00:00:00 JST 2019, homeTown=Tatween, species=Humain, ageAvg=100, apprentice=Asoka Tano)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A6, price=B6, tax=C6, usingStartDate=D6, homeTown=E6, species=F6, ageAvg=G6, apprentice=H6}, parent=null, userName=Padme Amidara, price=120002, tax=2.08, usingStartDate=Sun Mar 03 00:00:00 JST 2019, homeTown=Naboo, species=Humain, ageAvg=100, apprentice=--)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A7, price=B7, tax=C7, usingStartDate=D7, homeTown=E7, species=F7, ageAvg=G7, apprentice=H7}, parent=null, userName=Luke Skywalker, price=120003, tax=3.08, usingStartDate=Mon Mar 04 00:00:00 JST 2019, homeTown=Police Masa, species=Humain, ageAvg=100, apprentice=Caire Ren)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A8, price=B8, tax=C8, usingStartDate=D8, homeTown=E8, species=F8, ageAvg=G8, apprentice=H8}, parent=null, userName=Caire Ren, price=120004, tax=4.08, usingStartDate=Tue Mar 05 00:00:00 JST 2019, homeTown=Chandrila, species=Humain, ageAvg=100, apprentice=--)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A9, price=B9, tax=C9, usingStartDate=D9, homeTown=E9, species=F9, ageAvg=G9, apprentice=H9}, parent=null, userName=Asoka Tano, price=120005, tax=5.08, usingStartDate=Wed Mar 06 00:00:00 JST 2019, homeTown=Siri, species=Togruta, ageAvg=100, apprentice=--)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A10, price=B10, tax=C10, usingStartDate=D10, homeTown=E10, species=F10, ageAvg=G10, apprentice=H10}, parent=null, userName=Darth Mall, price=120006, tax=6.08, usingStartDate=Thu Mar 07 00:00:00 JST 2019, homeTown=Dasomie, species=Zabrak, ageAvg=null, apprentice=Opres sauvages)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A11, price=B11, tax=C11, usingStartDate=D11, homeTown=E11, species=F11, ageAvg=G11, apprentice=H11}, parent=null, userName=Yoda, price=120007, tax=7.08, usingStartDate=Fri Mar 08 00:00:00 JST 2019, homeTown=--, species=Yodaの種族, ageAvg=800, apprentice=Luke Skywalker)
com.example.demo.service.DemoService : UsingListRecord(positions={userName=A12, price=B12, tax=C12, usingStartDate=D12, homeTown=E12, species=F12, ageAvg=G12, apprentice=H12}, parent=null, userName=total, price=840028, tax=null, usingStartDate=null, homeTown=--, species=null, ageAvg=null, apprentice=--)

Épilogue

J'ai remarqué qu'il a été mis en œuvre, mais il est également bon que le processus de clôture ne soit pas nécessaire. Cette fois, c'est devenu un peu compliqué car le processus de sortie de la formule (ligne totale) est inclus dans l'enregistrement final, mais s'il n'y a pas de formule, la méthode n'est pas nécessaire pour les classes Sheet et Record, et l'annotation est réduite et c'est assez simple. Devenir.

Recommended Posts

Sortie d'Excel avec des formules avec XlsMapper
Sortie FizzBuzz avec flux
Sortie de fichier csv avec csv ouvert
Exploitons Excel avec Java! !!
Sortie CSV par Apache Commons CSV
Sortie quatre-vingt-dix-neuf avec Stream
Contrôle de la sortie du journal avec Doma2
Sortie "Izumi Oishi" avec dokojava
Manipuler Excel avec Apache POI
Sortie vers Excel en utilisant Apache POI!
Caractères de sortie comme une conversation avec JavaFX
Couverture de test de sortie avec Clover + Gradle
Créer un fichier Excel avec POI
Sortie PDF et TIFF avec Java 8
Exemple de mise à jour de fichier EXCEL avec JAVA
Java ajoute et lit des formules Excel