From 006a48e314f5d512b1add1eb8468a9051b6e1e93 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Sat, 2 Dec 2023 02:02:52 +0200 Subject: [PATCH 1/3] Default column name to pojo field name, add allFields mode to Measurement annotation, and add Exclude annotation. --- .../java/org/influxdb/annotation/Column.java | 5 +- .../java/org/influxdb/annotation/Exclude.java | 38 +++++ .../org/influxdb/annotation/Measurement.java | 7 + src/main/java/org/influxdb/dto/Point.java | 44 ++++-- .../influxdb/impl/InfluxDBResultMapper.java | 54 ++++--- src/test/java/org/influxdb/dto/PointTest.java | 141 ++++++++++++++++-- .../impl/InfluxDBResultMapperTest.java | 56 +++++-- 7 files changed, 283 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/influxdb/annotation/Exclude.java diff --git a/src/main/java/org/influxdb/annotation/Column.java b/src/main/java/org/influxdb/annotation/Column.java index cde2fbe50..2837f5d43 100644 --- a/src/main/java/org/influxdb/annotation/Column.java +++ b/src/main/java/org/influxdb/annotation/Column.java @@ -32,7 +32,10 @@ @Target(ElementType.FIELD) public @interface Column { - String name(); + /** + * If unset, the annotated field's name will be used as the column name + */ + String name() default ""; boolean tag() default false; } diff --git a/src/main/java/org/influxdb/annotation/Exclude.java b/src/main/java/org/influxdb/annotation/Exclude.java new file mode 100644 index 000000000..01e6f52e6 --- /dev/null +++ b/src/main/java/org/influxdb/annotation/Exclude.java @@ -0,0 +1,38 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 azeti Networks AG () + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package org.influxdb.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * When a POJO annotated with {@code @Measurement(allFields = true)} is loaded or saved, + * this annotation can be used to exclude some of its fields. + * @see Measurement#allFields() + * + * @author Eran Leshem + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Exclude { +} diff --git a/src/main/java/org/influxdb/annotation/Measurement.java b/src/main/java/org/influxdb/annotation/Measurement.java index a834bfbae..fa9d19fd5 100644 --- a/src/main/java/org/influxdb/annotation/Measurement.java +++ b/src/main/java/org/influxdb/annotation/Measurement.java @@ -40,4 +40,11 @@ String retentionPolicy() default "autogen"; TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * If {@code true}, then all non-static fields of this measurement will be loaded or saved, + * regardless of any {@code @Column} annotations. + * @see Exclude + */ + boolean allFields() default false; } diff --git a/src/main/java/org/influxdb/dto/Point.java b/src/main/java/org/influxdb/dto/Point.java index 96069026c..b8cc9e2e6 100755 --- a/src/main/java/org/influxdb/dto/Point.java +++ b/src/main/java/org/influxdb/dto/Point.java @@ -1,7 +1,15 @@ package org.influxdb.dto; +import org.influxdb.BuilderException; +import org.influxdb.annotation.Column; +import org.influxdb.annotation.Exclude; +import org.influxdb.annotation.Measurement; +import org.influxdb.annotation.TimeColumn; +import org.influxdb.impl.Preconditions; + import java.lang.annotation.Annotation; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -11,14 +19,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import java.util.TreeMap; import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.TimeUnit; -import org.influxdb.BuilderException; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; -import org.influxdb.annotation.TimeColumn; -import org.influxdb.impl.Preconditions; /** * Representation of a InfluxDB database Point. @@ -276,23 +279,34 @@ public boolean hasFields() { */ public Builder addFieldsFromPOJO(final Object pojo) { - Class clazz = pojo.getClass(); + Class clazz = pojo.getClass(); + Measurement measurement = clazz.getAnnotation(Measurement.class); + boolean allFields = measurement != null && measurement.allFields(); + while (clazz != null) { for (Field field : clazz.getDeclaredFields()) { Column column = field.getAnnotation(Column.class); - if (column == null) { + if (column == null && !(allFields + && !field.isAnnotationPresent(Exclude.class) && !Modifier.isStatic(field.getModifiers()))) { continue; } field.setAccessible(true); - String fieldName = column.name(); - addFieldByAttribute(pojo, field, column, fieldName); + + String fieldName; + if (column != null && !column.name().isEmpty()) { + fieldName = column.name(); + } else { + fieldName = field.getName(); + } + + addFieldByAttribute(pojo, field, column != null && column.tag(), fieldName); } - clazz = clazz.getSuperclass(); - } + clazz = clazz.getSuperclass(); + } if (this.fields.isEmpty()) { throw new BuilderException("Class " + pojo.getClass().getName() @@ -302,8 +316,8 @@ public Builder addFieldsFromPOJO(final Object pojo) { return this; } - private void addFieldByAttribute(final Object pojo, final Field field, final Column column, - final String fieldName) { + private void addFieldByAttribute(final Object pojo, final Field field, final boolean tag, + final String fieldName) { try { Object fieldValue = field.get(pojo); @@ -325,7 +339,7 @@ private void addFieldByAttribute(final Object pojo, final Field field, final Col return; } - if (column.tag()) { + if (tag) { if (fieldValue != null) { this.tags.put(fieldName, (String) fieldValue); } diff --git a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java index d6edef17c..08b9f6546 100644 --- a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java +++ b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java @@ -20,7 +20,14 @@ */ package org.influxdb.impl; +import org.influxdb.InfluxDBMapperException; +import org.influxdb.annotation.Column; +import org.influxdb.annotation.Exclude; +import org.influxdb.annotation.Measurement; +import org.influxdb.dto.QueryResult; + import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -33,11 +40,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; -import org.influxdb.InfluxDBMapperException; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; -import org.influxdb.dto.QueryResult; - /** * Main class responsible for mapping a QueryResult to a POJO. * @@ -213,13 +215,19 @@ void cacheMeasurementClass(final Class... classVarAgrs) { } ConcurrentMap influxColumnAndFieldMap = new ConcurrentHashMap<>(); + Measurement measurement = clazz.getAnnotation(Measurement.class); + boolean allFields = measurement != null && measurement.allFields(); + Class c = clazz; while (c != null) { for (Field field : c.getDeclaredFields()) { Column colAnnotation = field.getAnnotation(Column.class); - if (colAnnotation != null) { - influxColumnAndFieldMap.put(colAnnotation.name(), field); + if (colAnnotation == null && !(allFields + && !field.isAnnotationPresent(Exclude.class) && !Modifier.isStatic(field.getModifiers()))) { + continue; } + + influxColumnAndFieldMap.put(getFieldName(field, colAnnotation), field); } c = c.getSuperclass(); } @@ -227,6 +235,14 @@ void cacheMeasurementClass(final Class... classVarAgrs) { } } + private static String getFieldName(final Field field, final Column colAnnotation) { + if (colAnnotation != null && !colAnnotation.name().isEmpty()) { + return colAnnotation.name(); + } + + return field.getName(); + } + String getMeasurementName(final Class clazz) { return ((Measurement) clazz.getAnnotation(Measurement.class)).name(); } @@ -289,17 +305,11 @@ List parseSeriesAs(final QueryResult.Series series, final Class clazz, /** * InfluxDB client returns any number as Double. - * See https://github.com/influxdata/influxdb-java/issues/153#issuecomment-259681987 + * See ... * for more information. * - * @param object - * @param field - * @param value - * @param precision - * @throws IllegalArgumentException - * @throws IllegalAccessException */ - void setFieldValue(final T object, final Field field, final Object value, final TimeUnit precision) + private static void setFieldValue(final T object, final Field field, final Object value, final TimeUnit precision) throws IllegalArgumentException, IllegalAccessException { if (value == null) { return; @@ -325,8 +335,8 @@ void setFieldValue(final T object, final Field field, final Object value, fi } } - boolean fieldValueModified(final Class fieldType, final Field field, final T object, final Object value, - final TimeUnit precision) + static boolean fieldValueModified(final Class fieldType, final Field field, final T object, final Object value, + final TimeUnit precision) throws IllegalArgumentException, IllegalAccessException { if (String.class.isAssignableFrom(fieldType)) { field.set(object, String.valueOf(value)); @@ -351,8 +361,8 @@ boolean fieldValueModified(final Class fieldType, final Field field, fina return false; } - boolean fieldValueForPrimitivesModified(final Class fieldType, final Field field, final T object, - final Object value) throws IllegalArgumentException, IllegalAccessException { + static boolean fieldValueForPrimitivesModified(final Class fieldType, final Field field, final T object, + final Object value) throws IllegalArgumentException, IllegalAccessException { if (double.class.isAssignableFrom(fieldType)) { field.setDouble(object, ((Double) value).doubleValue()); return true; @@ -372,8 +382,8 @@ boolean fieldValueForPrimitivesModified(final Class fieldType, final Fiel return false; } - boolean fieldValueForPrimitiveWrappersModified(final Class fieldType, final Field field, final T object, - final Object value) throws IllegalArgumentException, IllegalAccessException { + static boolean fieldValueForPrimitiveWrappersModified(final Class fieldType, final Field field, final T object, + final Object value) throws IllegalArgumentException, IllegalAccessException { if (Double.class.isAssignableFrom(fieldType)) { field.set(object, value); return true; @@ -393,7 +403,7 @@ boolean fieldValueForPrimitiveWrappersModified(final Class fieldType, fin return false; } - private Long toMillis(final long value, final TimeUnit precision) { + private static Long toMillis(final long value, final TimeUnit precision) { return TimeUnit.MILLISECONDS.convert(value, precision); } diff --git a/src/test/java/org/influxdb/dto/PointTest.java b/src/test/java/org/influxdb/dto/PointTest.java index a49454177..c315aad23 100755 --- a/src/test/java/org/influxdb/dto/PointTest.java +++ b/src/test/java/org/influxdb/dto/PointTest.java @@ -1,7 +1,17 @@ package org.influxdb.dto; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; +import org.influxdb.BuilderException; +import org.influxdb.InfluxDB; +import org.influxdb.annotation.Column; +import org.influxdb.annotation.Measurement; +import org.influxdb.annotation.TimeColumn; +import org.influxdb.impl.InfluxDBImpl; +import org.junit.Assert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.lang.reflect.Field; import java.math.BigDecimal; @@ -16,18 +26,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import org.influxdb.BuilderException; -import org.influxdb.InfluxDB; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; -import org.influxdb.annotation.TimeColumn; -import org.influxdb.impl.InfluxDBImpl; -import org.junit.Assert; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; -import org.mockito.Mockito; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Test for the Point DTO. @@ -794,6 +794,63 @@ public void testAddFieldsFromPOJOWithPublicAttributes() { Assertions.assertEquals(pojo.uuid, p.getTags().get("uuid")); } + @Test + public void testAddFieldsFromPojoWithAllFieldsAnnotation() { + + PojoWithAllFieldsAnnotation pojo = new PojoWithAllFieldsAnnotation(); + pojo.booleanObject = true; + pojo.booleanPrimitive = false; + pojo.doubleObject = 2.0; + pojo.doublePrimitive = 3.1; + pojo.integerObject = 32; + pojo.integerPrimitive = 64; + pojo.longObject = 1L; + pojo.longPrimitive = 2L; + pojo.time = Instant.now(); + pojo.uuid = "TEST"; + + Point p = Point.measurementByPOJO(PojoWithAllFieldsAnnotation.class).addFieldsFromPOJO(pojo).build(); + + assertThat(p.lineProtocol()).startsWith("mymeasurement"); + assertThat(p.getFields()).doesNotContainKey("staticField"); + Assertions.assertEquals(pojo.booleanObject, p.getFields().get("booleanObject")); + Assertions.assertEquals(pojo.booleanPrimitive, p.getFields().get("booleanPrimitive")); + Assertions.assertEquals(pojo.doubleObject, p.getFields().get("doubleObject")); + Assertions.assertEquals(pojo.doublePrimitive, p.getFields().get("doublePrimitive")); + Assertions.assertEquals(pojo.integerObject, p.getFields().get("integerObject")); + Assertions.assertEquals(pojo.integerPrimitive, p.getFields().get("integerPrimitive")); + Assertions.assertEquals(pojo.longObject, p.getFields().get("longObject")); + Assertions.assertEquals(pojo.longPrimitive, p.getFields().get("longPrimitive")); + Assertions.assertEquals(pojo.uuid, p.getTags().get("uuid")); + } + + @Test + public void testAddFieldsFromPojoWithBlankColumnAnnotations() { + PojoWithBlankColumnAnnotations pojo = new PojoWithBlankColumnAnnotations(); + pojo.booleanObject = true; + pojo.booleanPrimitive = false; + pojo.doubleObject = 2.0; + pojo.doublePrimitive = 3.1; + pojo.integerObject = 32; + pojo.integerPrimitive = 64; + pojo.longObject = 1L; + pojo.longPrimitive = 2L; + pojo.time = Instant.now(); + pojo.uuid = "TEST"; + + Point p = Point.measurementByPOJO(PojoWithBlankColumnAnnotations.class).addFieldsFromPOJO(pojo).build(); + + Assertions.assertEquals(pojo.booleanObject, p.getFields().get("booleanObject")); + Assertions.assertEquals(pojo.booleanPrimitive, p.getFields().get("booleanPrimitive")); + Assertions.assertEquals(pojo.doubleObject, p.getFields().get("doubleObject")); + Assertions.assertEquals(pojo.doublePrimitive, p.getFields().get("doublePrimitive")); + Assertions.assertEquals(pojo.integerObject, p.getFields().get("integerObject")); + Assertions.assertEquals(pojo.integerPrimitive, p.getFields().get("integerPrimitive")); + Assertions.assertEquals(pojo.longObject, p.getFields().get("longObject")); + Assertions.assertEquals(pojo.longPrimitive, p.getFields().get("longPrimitive")); + Assertions.assertEquals(pojo.uuid, p.getTags().get("uuid")); + } + @Test public void testInheritMeasurement() { Point expected = Point.measurementByPOJO(SubClassMeasurement.class) @@ -1007,6 +1064,62 @@ static class PojoWithPublicAttributes { public Boolean booleanObject; } + @Measurement(name = "mymeasurement", allFields = true) + static class PojoWithAllFieldsAnnotation { + public static final String staticField = "static"; + + public boolean booleanPrimitive; + + @TimeColumn + public Instant time; + + @Column(tag = true) + public String uuid; + + public Double doubleObject; + public Long longObject; + public Integer integerObject; + public double doublePrimitive; + public long longPrimitive; + public int integerPrimitive; + public Boolean booleanObject; + } + + @Measurement(name = "mymeasurement") + static class PojoWithBlankColumnAnnotations { + + @Column + public boolean booleanPrimitive; + + @Column + @TimeColumn + public Instant time; + + @Column(tag = true) + public String uuid; + + @Column + public Double doubleObject; + + @Column + public Long longObject; + + @Column + public Integer integerObject; + + @Column + public double doublePrimitive; + + @Column + public long longPrimitive; + + @Column + public int integerPrimitive; + + @Column + public Boolean booleanObject; + } + @Measurement(name = "SuperMeasuremet") static class SuperMeasurement { @Column(name = "superClassField") diff --git a/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java b/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java index f4eee0ba2..d93c4d305 100644 --- a/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java +++ b/src/test/java/org/influxdb/impl/InfluxDBResultMapperTest.java @@ -20,6 +20,17 @@ */ package org.influxdb.impl; +import org.influxdb.InfluxDBMapperException; +import org.influxdb.annotation.Column; +import org.influxdb.annotation.Exclude; +import org.influxdb.annotation.Measurement; +import org.influxdb.annotation.TimeColumn; +import org.influxdb.dto.QueryResult; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + import java.time.Instant; import java.util.Arrays; import java.util.Date; @@ -31,16 +42,6 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; -import org.influxdb.InfluxDBMapperException; -import org.influxdb.annotation.Column; -import org.influxdb.annotation.Measurement; -import org.influxdb.annotation.TimeColumn; -import org.influxdb.dto.QueryResult; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; - /** * @author fmachado */ @@ -71,6 +72,12 @@ public void testToPOJO_HappyPath() { // Then... Assertions.assertEquals(1, myList.size(), "there must be one entry in the result list"); + + //When... + List myList1 = mapper.toPOJO(queryResult, MyAllFieldsCustomMeasurement.class); + + // Then... + Assertions.assertEquals(1, myList1.size(), "there must be one entry in the result list"); } @Test @@ -578,6 +585,35 @@ public String toString() { } } + @Measurement(name = "CustomMeasurement", allFields = true) + static class MyAllFieldsCustomMeasurement { + private Instant time; + private String uuid; + private Double doubleObject; + private Long longObject; + private Integer integerObject; + private double doublePrimitive; + private long longPrimitive; + private int integerPrimitive; + private Boolean booleanObject; + private boolean booleanPrimitive; + + @SuppressWarnings("unused") + @Exclude + private String nonColumn1; + + @SuppressWarnings("unused") + @Exclude + private Random rnd; + + @Override + public String toString() { + return "MyCustomMeasurement [time=" + time + ", uuid=" + uuid + ", doubleObject=" + doubleObject + ", longObject=" + longObject + + ", integerObject=" + integerObject + ", doublePrimitive=" + doublePrimitive + ", longPrimitive=" + longPrimitive + + ", integerPrimitive=" + integerPrimitive + ", booleanObject=" + booleanObject + ", booleanPrimitive=" + booleanPrimitive + "]"; + } + } + @Measurement(name = "SuperMeasurement") static class MySuperMeasurement { From 47554a29a1de4dfe5469b9747b8bb1f188e007e8 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:52:20 +0200 Subject: [PATCH 2/3] Fixed style issues --- src/main/java/org/influxdb/annotation/Column.java | 2 +- src/main/java/org/influxdb/impl/InfluxDBResultMapper.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/influxdb/annotation/Column.java b/src/main/java/org/influxdb/annotation/Column.java index 2837f5d43..6edb256f8 100644 --- a/src/main/java/org/influxdb/annotation/Column.java +++ b/src/main/java/org/influxdb/annotation/Column.java @@ -33,7 +33,7 @@ public @interface Column { /** - * If unset, the annotated field's name will be used as the column name + * If unset, the annotated field's name will be used as the column name. */ String name() default ""; diff --git a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java index 08b9f6546..15d4c283a 100644 --- a/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java +++ b/src/main/java/org/influxdb/impl/InfluxDBResultMapper.java @@ -362,7 +362,8 @@ static boolean fieldValueModified(final Class fieldType, final Field fiel } static boolean fieldValueForPrimitivesModified(final Class fieldType, final Field field, final T object, - final Object value) throws IllegalArgumentException, IllegalAccessException { + final Object value) + throws IllegalArgumentException, IllegalAccessException { if (double.class.isAssignableFrom(fieldType)) { field.setDouble(object, ((Double) value).doubleValue()); return true; @@ -383,7 +384,8 @@ static boolean fieldValueForPrimitivesModified(final Class fieldType, fin } static boolean fieldValueForPrimitiveWrappersModified(final Class fieldType, final Field field, final T object, - final Object value) throws IllegalArgumentException, IllegalAccessException { + final Object value) + throws IllegalArgumentException, IllegalAccessException { if (Double.class.isAssignableFrom(fieldType)) { field.set(object, value); return true; From aeaba3272e5276551c8636ec3a314f8f0adca869 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:34:50 +0200 Subject: [PATCH 3/3] Updated manual --- MANUAL.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/MANUAL.md b/MANUAL.md index 62d45d67a..18040aeab 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -323,21 +323,40 @@ public class Cpu { } ``` -2. Add @Measurement,@TimeColumn and @Column annotations: +2. Add @Measurement,@TimeColumn and @Column annotations (column names default to field names unless otherwise specified): ```Java @Measurement(name = "cpu") public class Cpu { @TimeColumn - @Column(name = "time") + @Column private Instant time; @Column(name = "host", tag = true) private String hostname; - @Column(name = "region", tag = true) + @Column(tag = true) + private String region; + @Column + private Double idle; + @Column + private Boolean happydevop; + @Column(name = "uptimesecs") + private Long uptimeSecs; + // getters (and setters if you need) +} +``` + +Alternatively, you can use: + +```Java +@Measurement(name = "cpu", allFields = true) +public class Cpu { + @TimeColumn + private Instant time; + @Column(name = "host", tag = true) + private String hostname; + @Column(tag = true) private String region; - @Column(name = "idle") private Double idle; - @Column(name = "happydevop") private Boolean happydevop; @Column(name = "uptimesecs") private Long uptimeSecs;