| 1 | package com.reallifedeveloper.tools.test.database; | |
| 2 | ||
| 3 | import java.io.Serializable; | |
| 4 | import java.lang.reflect.Constructor; | |
| 5 | import java.lang.reflect.Field; | |
| 6 | import java.lang.reflect.ParameterizedType; | |
| 7 | import java.lang.reflect.Type; | |
| 8 | import java.math.BigDecimal; | |
| 9 | import java.math.BigInteger; | |
| 10 | import java.time.LocalDate; | |
| 11 | import java.time.LocalDateTime; | |
| 12 | import java.time.ZonedDateTime; | |
| 13 | import java.util.ArrayList; | |
| 14 | import java.util.Arrays; | |
| 15 | import java.util.Collection; | |
| 16 | import java.util.Date; | |
| 17 | import java.util.HashSet; | |
| 18 | import java.util.List; | |
| 19 | import java.util.Map; | |
| 20 | import java.util.Optional; | |
| 21 | import java.util.Set; | |
| 22 | import java.util.UUID; | |
| 23 | ||
| 24 | import org.checkerframework.checker.nullness.qual.Nullable; | |
| 25 | import org.slf4j.Logger; | |
| 26 | import org.slf4j.LoggerFactory; | |
| 27 | import org.springframework.data.repository.CrudRepository; | |
| 28 | ||
| 29 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | |
| 30 | import jakarta.persistence.Embeddable; | |
| 31 | import jakarta.persistence.Entity; | |
| 32 | import jakarta.persistence.Id; | |
| 33 | import jakarta.persistence.JoinColumn; | |
| 34 | import jakarta.persistence.JoinTable; | |
| 35 | import jakarta.persistence.OneToMany; | |
| 36 | import jakarta.persistence.OneToOne; | |
| 37 | import lombok.Getter; | |
| 38 | ||
| 39 | import com.reallifedeveloper.tools.test.TestUtil; | |
| 40 | ||
| 41 | /** | |
| 42 | * A helper class to write data into a {@link CrudRepository} from some data source, e.g., a CSV file, where each entity is represented by a | |
| 43 | * {@link DbTableRow}. | |
| 44 | * <p> | |
| 45 | * This can be useful for inserting test data into a repository, irrespective of whether the repository connects to a real database or not. | |
| 46 | * <p> | |
| 47 | * TODO: The current implementation only has basic support for "to many" associations (there must be a &JoinTable annotation on a field, | |
| 48 | * with &JoinColumn annotations), and for enums (an enum must be stored as a string). | |
| 49 | * | |
| 50 | * @author RealLifeDeveloper | |
| 51 | */ | |
| 52 | @Getter | |
| 53 | @SuppressWarnings("PMD") | |
| 54 | @SuppressFBWarnings(value = { "CRLF_INJECTION_LOGS", "IMPROPER_UNICODE" }) | |
| 55 | public class CrudRepositoryWriter { | |
| 56 | ||
| 57 | private static final Logger LOG = LoggerFactory.getLogger(CrudRepositoryWriter.class); | |
| 58 | ||
| 59 | private final Set<Class<?>> classes = new HashSet<>(); | |
| 60 | private final List<Object> entities = new ArrayList<>(); | |
| 61 | ||
| 62 | /** | |
| 63 | * Creates a new entity based on data from a {@link DbTableRow} and writes it into a repository if appropriate. | |
| 64 | * <p> | |
| 65 | * This method may create entities that are not directly handled by the repository, in which case they are assumed to be related to some | |
| 66 | * entity in the repository. | |
| 67 | * | |
| 68 | * @param <T> the type of entities in the repository | |
| 69 | * @param <E> the type of entity being created | |
| 70 | * @param <ID> the type of the primary key of the entities in the repository | |
| 71 | * @param tableRow the {@code TableRow} with the data to insert into the fields of the newly created entity | |
| 72 | * @param repositoryEntityType the class object representing {@code T}, i.e., the type ofrepository entities | |
| 73 | * @param entityType the class object representing {@code E}, i.e., the type of entity being created, or {@code null} | |
| 74 | * @param repository the repository in which to insert the newly created entity | |
| 75 | * @param tableName the name of the database table where the entity should be stored | |
| 76 | * @return {@code true} if an entity was created, no matter if it was saved in the repository, {@code false} otherwise | |
| 77 | * @throws ReflectiveOperationException if some reflection operation failed creating the entity or setting is fields | |
| 78 | */ | |
| 79 | public <T, E, ID extends Serializable> boolean writeEntity(DbTableRow tableRow, Class<T> repositoryEntityType, | |
| 80 | @Nullable Class<E> entityType, CrudRepository<T, ID> repository, String tableName) throws ReflectiveOperationException { | |
| 81 |
1
1. writeEntity : negated conditional → KILLED |
if (entityType == null) { |
| 82 |
1
1. writeEntity : replaced boolean return with true for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::writeEntity → KILLED |
return false; |
| 83 | } | |
| 84 |
1
1. writeEntity : negated conditional → KILLED |
if (entityType.getAnnotation(Embeddable.class) != null) { |
| 85 |
2
1. writeEntity : replaced boolean return with true for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::writeEntity → NO_COVERAGE 2. writeEntity : replaced boolean return with false for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::writeEntity → NO_COVERAGE |
return writeEmbeddable(tableRow, repositoryEntityType, entityType, repository, tableName); |
| 86 | } | |
| 87 |
2
1. writeEntity : negated conditional → KILLED 2. writeEntity : negated conditional → KILLED |
if (entityType.getAnnotation(Entity.class) == null || !JpaUtil.getTableName(entityType).equalsIgnoreCase(tableName)) { |
| 88 |
1
1. writeEntity : replaced boolean return with true for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::writeEntity → KILLED |
return false; |
| 89 | } | |
| 90 | E entity = createEntity(entityType); | |
| 91 | for (DbTableField column : tableRow.columns()) { | |
| 92 | String fieldName = JpaUtil.getFieldName(column.name(), entityType); | |
| 93 |
1
1. writeEntity : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::setField → KILLED |
setField(entity, fieldName, column.value()); |
| 94 | } | |
| 95 | LOG.debug("Saving entity {}", entity); | |
| 96 | entities.add(entity); | |
| 97 | classes.add(entity.getClass()); | |
| 98 |
1
1. writeEntity : negated conditional → KILLED |
if (entityType.equals(repositoryEntityType)) { |
| 99 | T entityToSave = repositoryEntityType.cast(entity); | |
| 100 | repository.save(entityToSave); | |
| 101 | } | |
| 102 |
1
1. writeEntity : replaced boolean return with false for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::writeEntity → SURVIVED |
return true; |
| 103 | } | |
| 104 | ||
| 105 | @SuppressWarnings("UnusedVariable") | |
| 106 | private <T, E, ID extends Serializable> boolean writeEmbeddable(DbTableRow tableRow, Class<T> repositoryEntityType, | |
| 107 | @Nullable Class<E> entityType, CrudRepository<T, ID> repository, String tableName) { | |
| 108 | LOG.debug("Saving embeddable {}", tableRow); | |
| 109 | throw new UnsupportedOperationException("writeEmbeddable not yet implemented"); | |
| 110 | } | |
| 111 | ||
| 112 | /** | |
| 113 | * Connects entities based on data in a join table. | |
| 114 | * | |
| 115 | * @param tableRow the {@code TableRow} with the data for the join table | |
| 116 | * @param joinTtableName the name of the join table to use to connect entities | |
| 117 | */ | |
| 118 | public void addEntitiesFromJoinTable(DbTableRow tableRow, String joinTtableName) { | |
| 119 |
1
1. addEntitiesFromJoinTable : removed call to java/util/Optional::ifPresent → KILLED |
joinTableField(joinTtableName).ifPresent(joinTableField -> { |
| 120 |
1
1. lambda$addEntitiesFromJoinTable$0 : removed call to java/lang/reflect/Field::setAccessible → KILLED |
joinTableField.setAccessible(true); |
| 121 | ParameterizedType parameterizedType = (ParameterizedType) joinTableField.getGenericType(); | |
| 122 | Class<?> targetType = (Class<?>) parameterizedType.getActualTypeArguments()[0]; | |
| 123 | JoinTable joinTable = joinTableField.getAnnotation(JoinTable.class); | |
| 124 | assert joinTable != null : "JoinTable annotation should be present when the joinTableField method returns a non-empty value"; | |
| 125 | for (JoinColumn joinColumn : joinTable.joinColumns()) { | |
| 126 | for (JoinColumn inverseJoinColumn : joinTable.inverseJoinColumns()) { | |
| 127 |
1
1. lambda$addEntitiesFromJoinTable$0 : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::addEntityFromJoinTable → KILLED |
addEntityFromJoinTable(tableRow, joinTableField, targetType, joinColumn, inverseJoinColumn); |
| 128 | } | |
| 129 | } | |
| 130 | }); | |
| 131 | } | |
| 132 | ||
| 133 | /** | |
| 134 | * Goes through all entities that have been saved, trying to fix missing associations. | |
| 135 | * | |
| 136 | * @throws ReflectiveOperationException if something went wrong using reflection to analyze the entities | |
| 137 | */ | |
| 138 | public void fillReferencesBetweenEntities() throws ReflectiveOperationException { | |
| 139 | for (Object entity : entities) { | |
| 140 | for (Field field : entity.getClass().getDeclaredFields()) { | |
| 141 |
1
1. fillReferencesBetweenEntities : removed call to java/lang/reflect/Field::setAccessible → KILLED |
field.setAccessible(true); |
| 142 | OneToOne oneToOne = field.getAnnotation(OneToOne.class); | |
| 143 |
1
1. fillReferencesBetweenEntities : negated conditional → KILLED |
if (oneToOne != null) { |
| 144 |
1
1. fillReferencesBetweenEntities : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::handleOneToOne → KILLED |
handleOneToOne(entity, field, oneToOne); |
| 145 | } | |
| 146 | OneToMany oneToMany = field.getAnnotation(OneToMany.class); | |
| 147 |
1
1. fillReferencesBetweenEntities : negated conditional → KILLED |
if (oneToMany != null) { |
| 148 |
1
1. fillReferencesBetweenEntities : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::handleOneToMany → KILLED |
handleOneToMany(entity, field, oneToMany); |
| 149 | } | |
| 150 | } | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | private void handleOneToOne(Object entity, Field field, OneToOne oneToOne) throws IllegalAccessException, NoSuchFieldException { | |
| 155 | String mappedBy = oneToOne.mappedBy(); | |
| 156 |
2
1. handleOneToOne : negated conditional → SURVIVED 2. handleOneToOne : negated conditional → KILLED |
if (mappedBy == null || mappedBy.isEmpty()) { |
| 157 | JoinColumn joinColumn = field.getAnnotation(JoinColumn.class); | |
| 158 |
1
1. handleOneToOne : negated conditional → KILLED |
if (joinColumn != null) { |
| 159 | mappedBy = JpaUtil.getFieldName(joinColumn.name(), field.getType()); | |
| 160 | } | |
| 161 | } | |
| 162 |
2
1. handleOneToOne : negated conditional → KILLED 2. handleOneToOne : negated conditional → KILLED |
if (mappedBy == null || mappedBy.isEmpty()) { |
| 163 | throw new IllegalStateException("OneToOne field " + entity.getClass().getName() + "." + field.getName() | |
| 164 | + " has no mappedBy and no JoinColumn annotation"); | |
| 165 | } | |
| 166 | Object id = JpaUtil.getIdValue(entity); | |
| 167 | List<?> entitiesToMap = findEntitiesByClassAndField(field.getType(), mappedBy, id); | |
| 168 |
2
1. handleOneToOne : changed conditional boundary → KILLED 2. handleOneToOne : negated conditional → KILLED |
if (entitiesToMap.size() > 1) { |
| 169 | throw new IllegalStateException("Found multiple candidates for OneToOne mapping: entity=" + entity + ", field={}" + field); | |
| 170 | } | |
| 171 |
1
1. handleOneToOne : negated conditional → KILLED |
Object value = entitiesToMap.isEmpty() ? null : entitiesToMap.get(0); |
| 172 | LOG.debug("Setting OneToOne field {} to {}", entity.getClass().getName() + "." + field.getName(), value); | |
| 173 |
1
1. handleOneToOne : removed call to java/lang/reflect/Field::set → KILLED |
field.set(entity, value); |
| 174 | } | |
| 175 | ||
| 176 | private void handleOneToMany(Object entity, Field field, OneToMany oneToMany) throws IllegalAccessException, NoSuchFieldException { | |
| 177 | Class<?> collectionType = field.getType(); | |
| 178 |
1
1. handleOneToMany : negated conditional → KILLED |
if (Collection.class.isAssignableFrom(collectionType)) { |
| 179 |
1
1. handleOneToMany : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::saveEntityInCollection → SURVIVED |
saveEntityInCollection(entity, field, oneToMany); |
| 180 |
1
1. handleOneToMany : negated conditional → KILLED |
} else if (Map.class.isAssignableFrom(collectionType)) { |
| 181 |
1
1. handleOneToMany : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::saveEntityInMap → KILLED |
saveEntityInMap(entity, field, oneToMany); |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | private void saveEntityInCollection(Object entity, Field field, OneToMany oneToMany) | |
| 186 | throws IllegalAccessException, NoSuchFieldException { | |
| 187 | LOG.trace("saveEntityInCollection: entity={}, field={}, oneToMany={}", entity, field, oneToMany); | |
| 188 | LOG.trace("Not yet implemented"); | |
| 189 | } | |
| 190 | ||
| 191 | private void saveEntityInMap(Object entity, Field field, OneToMany oneToMany) throws IllegalAccessException, NoSuchFieldException { | |
| 192 | LOG.trace("saveEntityInMap: entity={}, field={}, oneToMany={}", entity, field, oneToMany); | |
| 193 | ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); | |
| 194 | Type[] targetTypes = parameterizedType.getActualTypeArguments(); | |
| 195 | Class<?> targetClass = getClass(targetTypes[1].getTypeName()); | |
| 196 | String mappedBy = oneToMany.mappedBy(); | |
| 197 |
2
1. saveEntityInMap : negated conditional → SURVIVED 2. saveEntityInMap : negated conditional → KILLED |
if (mappedBy == null || mappedBy.isEmpty()) { |
| 198 | JoinColumn joinColumn = field.getAnnotation(JoinColumn.class); | |
| 199 |
1
1. saveEntityInMap : negated conditional → KILLED |
if (joinColumn != null) { |
| 200 | mappedBy = JpaUtil.getFieldName(joinColumn.name(), targetClass); | |
| 201 | } | |
| 202 | } | |
| 203 |
2
1. saveEntityInMap : negated conditional → KILLED 2. saveEntityInMap : negated conditional → KILLED |
if (mappedBy == null || mappedBy.isEmpty()) { |
| 204 | throw new IllegalStateException("OneToMany field " + entity.getClass().getName() + "." + field.getName() | |
| 205 | + " has no mappedBy and no JoinColumn annotation"); | |
| 206 | } | |
| 207 | Object id = JpaUtil.getIdValue(entity); | |
| 208 | List<?> entitiesToMap = findEntitiesByClassAndField(targetClass, mappedBy, id); | |
| 209 |
1
1. saveEntityInMap : removed call to com/reallifedeveloper/tools/test/database/JpaUtil::addEntitiesToMapField → KILLED |
JpaUtil.addEntitiesToMapField(field, entity, entitiesToMap); |
| 210 | } | |
| 211 | ||
| 212 | private Class<?> getClass(String className) { | |
| 213 | try { | |
| 214 |
1
1. getClass : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::getClass → KILLED |
return Class.forName(className); |
| 215 | } catch (ClassNotFoundException e) { | |
| 216 | throw new IllegalStateException("Class " + className + " not found", e); | |
| 217 | } | |
| 218 | } | |
| 219 | ||
| 220 | private <T> List<T> findEntitiesByClassAndField(Class<T> entityClass, String fieldName, Object value) | |
| 221 | throws IllegalAccessException, NoSuchFieldException { | |
| 222 | LOG.trace("Finding entities by class={}, field={} and value={}", entityClass, fieldName, value); | |
| 223 | List<T> foundEntities = new ArrayList<>(); | |
| 224 | for (T entity : entitiesOfType(entityClass)) { | |
| 225 | Field field = entity.getClass().getDeclaredField(fieldName); | |
| 226 |
1
1. findEntitiesByClassAndField : removed call to java/lang/reflect/Field::setAccessible → KILLED |
field.setAccessible(true); |
| 227 | // LOG.debug("{}.{}={}", entityClass.getName(), fieldName, field.get(entity)); | |
| 228 | Object fieldValue = field.get(entity); | |
| 229 |
1
1. findEntitiesByClassAndField : negated conditional → KILLED |
if (fieldValue == null) { |
| 230 | continue; | |
| 231 | } | |
| 232 |
1
1. findEntitiesByClassAndField : negated conditional → KILLED |
if (fieldValue.equals(value)) { |
| 233 | foundEntities.add(entity); | |
| 234 |
2
1. findEntitiesByClassAndField : negated conditional → KILLED 2. findEntitiesByClassAndField : negated conditional → KILLED |
} else if (fieldValue.getClass().getAnnotation(Entity.class) != null && JpaUtil.getIdValue(fieldValue).equals(value)) { |
| 235 | foundEntities.add(entity); | |
| 236 | } | |
| 237 | } | |
| 238 |
1
1. findEntitiesByClassAndField : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::findEntitiesByClassAndField → KILLED |
return foundEntities; |
| 239 | } | |
| 240 | ||
| 241 | @SuppressWarnings("unchecked") | |
| 242 | private <T> List<T> entitiesOfType(Class<T> entityType) { | |
| 243 | // LOG.debug("Getting entities of type {}", entityType.getName()); | |
| 244 |
3
1. lambda$entitiesOfType$1 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::lambda$entitiesOfType$1 → KILLED 2. lambda$entitiesOfType$1 : replaced boolean return with false for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::lambda$entitiesOfType$1 → KILLED 3. entitiesOfType : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::entitiesOfType → KILLED |
return (List<T>) entities.stream().filter(entity -> entity.getClass().equals(entityType)).toList(); |
| 245 | } | |
| 246 | ||
| 247 | private Optional<Field> joinTableField(String tableName) { | |
| 248 | for (Class<?> c : classes) { | |
| 249 | for (Field field : c.getDeclaredFields()) { | |
| 250 | JoinTable joinTable = field.getAnnotation(JoinTable.class); | |
| 251 |
2
1. joinTableField : negated conditional → KILLED 2. joinTableField : negated conditional → KILLED |
if (joinTable != null && tableName.equalsIgnoreCase(joinTable.name())) { |
| 252 |
1
1. joinTableField : replaced return value with Optional.empty for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::joinTableField → KILLED |
return Optional.of(field); |
| 253 | } | |
| 254 | } | |
| 255 | } | |
| 256 | return Optional.empty(); | |
| 257 | } | |
| 258 | ||
| 259 | private void addEntityFromJoinTable(DbTableRow tableRow, Field joinTableField, Class<?> targetType, JoinColumn joinColumn, | |
| 260 | JoinColumn inverseJoinColumn) { | |
| 261 | String lhsPrimaryKey = null; | |
| 262 | String rhsPrimaryKey = null; | |
| 263 | for (DbTableField column : tableRow.columns()) { | |
| 264 |
1
1. addEntityFromJoinTable : negated conditional → KILLED |
if (column.name().equalsIgnoreCase(joinColumn.name())) { |
| 265 | lhsPrimaryKey = column.value(); | |
| 266 |
1
1. addEntityFromJoinTable : negated conditional → KILLED |
} else if (column.name().equalsIgnoreCase(inverseJoinColumn.name())) { |
| 267 | rhsPrimaryKey = column.value(); | |
| 268 | } | |
| 269 | } | |
| 270 |
2
1. addEntityFromJoinTable : negated conditional → KILLED 2. addEntityFromJoinTable : negated conditional → KILLED |
if (lhsPrimaryKey == null || rhsPrimaryKey == null) { |
| 271 | throw new IllegalStateException("Failed to find join table: missing attribute in DBUnit XML file: '" + joinColumn.name() | |
| 272 | + "' or '" + inverseJoinColumn.name() + "'"); | |
| 273 | } | |
| 274 | Object lhs = findEntity(lhsPrimaryKey, joinTableField.getDeclaringClass()); | |
| 275 | Object rhs = findEntity(rhsPrimaryKey, targetType); | |
| 276 |
1
1. addEntityFromJoinTable : removed call to com/reallifedeveloper/tools/test/database/JpaUtil::addObjectToCollectionField → KILLED |
JpaUtil.addObjectToCollectionField(joinTableField, lhs, rhs); |
| 277 | } | |
| 278 | ||
| 279 | private <T> T createEntity(Class<T> entityType) throws ReflectiveOperationException { | |
| 280 | Constructor<T> constructor = entityType.getDeclaredConstructor(); | |
| 281 |
1
1. createEntity : removed call to java/lang/reflect/Constructor::setAccessible → KILLED |
constructor.setAccessible(true); |
| 282 |
1
1. createEntity : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createEntity → KILLED |
return constructor.newInstance(); |
| 283 | } | |
| 284 | ||
| 285 | private <T> void setField(T entity, String fieldName, String attributeValue) throws ReflectiveOperationException { | |
| 286 | Field field = JpaUtil.getField(entity, fieldName); | |
| 287 |
1
1. setField : removed call to java/lang/reflect/Field::setAccessible → SURVIVED |
field.setAccessible(true); |
| 288 | Object fieldValue = createObjectFromString(attributeValue, field, JpaUtil.getPrimaryKeyType(entity.getClass())); | |
| 289 | LOG.trace("Setting field {} to {}", fieldName, fieldValue); | |
| 290 |
1
1. setField : removed call to java/lang/reflect/Field::set → KILLED |
field.set(entity, fieldValue); |
| 291 |
2
1. setField : negated conditional → KILLED 2. setField : negated conditional → KILLED |
if (fieldValue != null && fieldValue.getClass().getAnnotation(Entity.class) != null) { |
| 292 |
1
1. setField : removed call to com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::potentiallyAddValueToCollection → KILLED |
potentiallyAddValueToCollection(fieldValue, fieldName, entity); |
| 293 | } | |
| 294 | } | |
| 295 | ||
| 296 | private <T> void potentiallyAddValueToCollection(Object entity, String fieldName, T value) { | |
| 297 | for (Field field : entity.getClass().getDeclaredFields()) { | |
| 298 |
1
1. potentiallyAddValueToCollection : removed call to java/lang/reflect/Field::setAccessible → KILLED |
field.setAccessible(true); |
| 299 | OneToMany oneToMany = field.getAnnotation(OneToMany.class); | |
| 300 |
1
1. potentiallyAddValueToCollection : negated conditional → KILLED |
if (oneToMany == null) { |
| 301 | continue; | |
| 302 | } | |
| 303 |
1
1. potentiallyAddValueToCollection : negated conditional → KILLED |
if (oneToMany.mappedBy().equals(fieldName)) { |
| 304 |
1
1. potentiallyAddValueToCollection : removed call to com/reallifedeveloper/tools/test/database/JpaUtil::addObjectToCollectionField → KILLED |
JpaUtil.addObjectToCollectionField(field, entity, value); |
| 305 | } | |
| 306 | } | |
| 307 | } | |
| 308 | ||
| 309 | private @Nullable Object createObjectFromString(String s, Field field, Class<?> primaryKeyType) { | |
| 310 | Class<?> type; | |
| 311 |
1
1. createObjectFromString : negated conditional → KILLED |
if (field.getAnnotation(Id.class) != null) { |
| 312 | type = primaryKeyType; | |
| 313 | } else { | |
| 314 | type = field.getType(); | |
| 315 | } | |
| 316 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return createObjectFromString(s, type); |
| 317 | } | |
| 318 | ||
| 319 | @SuppressWarnings("checkstyle:noReturnNull") | |
| 320 | private @Nullable Object createObjectFromString(String s, Class<?> type) { | |
| 321 |
2
1. createObjectFromString : negated conditional → KILLED 2. createObjectFromString : negated conditional → KILLED |
if (s == null || s.isEmpty()) { |
| 322 | return null; | |
| 323 | } | |
| 324 |
1
1. createObjectFromString : negated conditional → KILLED |
if (type == Byte.class) { |
| 325 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Byte.parseByte(s); |
| 326 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Short.class) { |
| 327 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Short.parseShort(s); |
| 328 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Integer.class) { |
| 329 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Integer.parseInt(s); |
| 330 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Long.class) { |
| 331 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Long.parseLong(s); |
| 332 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Float.class) { |
| 333 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Float.parseFloat(s); |
| 334 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Double.class) { |
| 335 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Double.parseDouble(s); |
| 336 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Boolean.class) { |
| 337 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Boolean.parseBoolean(s); |
| 338 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Character.class) { |
| 339 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return s.charAt(0); |
| 340 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == String.class) { |
| 341 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return s; |
| 342 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == Date.class) { |
| 343 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return TestUtil.parseDate(s); |
| 344 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == LocalDate.class) { |
| 345 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return LocalDate.parse(s); |
| 346 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == LocalDateTime.class) { |
| 347 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return LocalDateTime.parse(s); |
| 348 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == ZonedDateTime.class) { |
| 349 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return ZonedDateTime.parse(s); |
| 350 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == BigDecimal.class) { |
| 351 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return new BigDecimal(s); |
| 352 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == BigInteger.class) { |
| 353 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return new BigInteger(s); |
| 354 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == UUID.class) { |
| 355 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return UUID.fromString(s); |
| 356 |
1
1. createObjectFromString : negated conditional → KILLED |
} else if (type == List.class) { |
| 357 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return Arrays.asList(s.replaceAll("[{}]", "").split(",")); |
| 358 | } else { | |
| 359 |
1
1. createObjectFromString : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::createObjectFromString → KILLED |
return findEntity(s, type); |
| 360 | } | |
| 361 | } | |
| 362 | ||
| 363 | @SuppressWarnings({ "rawtypes", "unchecked" }) | |
| 364 | private Object findEntity(String strId, Class<?> entityType) { | |
| 365 |
1
1. findEntity : negated conditional → KILLED |
if (entityType.isEnum()) { |
| 366 | Class<? extends Enum> enumType = (Class<? extends Enum>) entityType; | |
| 367 |
1
1. findEntity : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::findEntity → KILLED |
return Enum.valueOf(enumType, strId); |
| 368 | } | |
| 369 | for (Object entity : entities) { | |
| 370 |
1
1. findEntity : negated conditional → KILLED |
if (entity.getClass().equals(entityType)) { |
| 371 | Field idField = JpaUtil.getIdField(entity); | |
| 372 |
1
1. findEntity : removed call to java/lang/reflect/Field::setAccessible → KILLED |
idField.setAccessible(true); |
| 373 | try { | |
| 374 | Object id = idField.get(entity); | |
| 375 |
2
1. findEntity : negated conditional → KILLED 2. findEntity : negated conditional → KILLED |
if (id != null && id.equals(createObjectFromString(strId, id.getClass()))) { |
| 376 |
1
1. findEntity : replaced return value with null for com/reallifedeveloper/tools/test/database/CrudRepositoryWriter::findEntity → KILLED |
return entity; |
| 377 | } | |
| 378 | } catch (IllegalAccessException e) { | |
| 379 | throw new IllegalStateException("Unexpected problem looking up entity of " + entityType + " with primary key " + strId, | |
| 380 | e); | |
| 381 | } | |
| 382 | } | |
| 383 | } | |
| 384 | throw new IllegalArgumentException("Entity of " + entityType + " with primary key " + strId + " not found"); | |
| 385 | } | |
| 386 | ||
| 387 | /** | |
| 388 | * Represents one row of data from the database. | |
| 389 | * | |
| 390 | * @author RealLifeDeveloper | |
| 391 | */ | |
| 392 | public interface DbTableRow { | |
| 393 | /** | |
| 394 | * Gives the fields of this row. | |
| 395 | * | |
| 396 | * @return the fields | |
| 397 | */ | |
| 398 | List<DbTableField> columns(); | |
| 399 | } | |
| 400 | ||
| 401 | /** | |
| 402 | * Represents the value of a single field in the database. | |
| 403 | * | |
| 404 | * @param name the name of the database column | |
| 405 | * @param value the value of the field | |
| 406 | */ | |
| 407 | public record DbTableField(String name, String value) { | |
| 408 | } | |
| 409 | } | |
Mutations | ||
| 81 |
1.1 |
|
| 82 |
1.1 |
|
| 84 |
1.1 |
|
| 85 |
1.1 2.2 |
|
| 87 |
1.1 2.2 |
|
| 88 |
1.1 |
|
| 93 |
1.1 |
|
| 98 |
1.1 |
|
| 102 |
1.1 |
|
| 119 |
1.1 |
|
| 120 |
1.1 |
|
| 127 |
1.1 |
|
| 141 |
1.1 |
|
| 143 |
1.1 |
|
| 144 |
1.1 |
|
| 147 |
1.1 |
|
| 148 |
1.1 |
|
| 156 |
1.1 2.2 |
|
| 158 |
1.1 |
|
| 162 |
1.1 2.2 |
|
| 168 |
1.1 2.2 |
|
| 171 |
1.1 |
|
| 173 |
1.1 |
|
| 178 |
1.1 |
|
| 179 |
1.1 |
|
| 180 |
1.1 |
|
| 181 |
1.1 |
|
| 197 |
1.1 2.2 |
|
| 199 |
1.1 |
|
| 203 |
1.1 2.2 |
|
| 209 |
1.1 |
|
| 214 |
1.1 |
|
| 226 |
1.1 |
|
| 229 |
1.1 |
|
| 232 |
1.1 |
|
| 234 |
1.1 2.2 |
|
| 238 |
1.1 |
|
| 244 |
1.1 2.2 3.3 |
|
| 251 |
1.1 2.2 |
|
| 252 |
1.1 |
|
| 264 |
1.1 |
|
| 266 |
1.1 |
|
| 270 |
1.1 2.2 |
|
| 276 |
1.1 |
|
| 281 |
1.1 |
|
| 282 |
1.1 |
|
| 287 |
1.1 |
|
| 290 |
1.1 |
|
| 291 |
1.1 2.2 |
|
| 292 |
1.1 |
|
| 298 |
1.1 |
|
| 300 |
1.1 |
|
| 303 |
1.1 |
|
| 304 |
1.1 |
|
| 311 |
1.1 |
|
| 316 |
1.1 |
|
| 321 |
1.1 2.2 |
|
| 324 |
1.1 |
|
| 325 |
1.1 |
|
| 326 |
1.1 |
|
| 327 |
1.1 |
|
| 328 |
1.1 |
|
| 329 |
1.1 |
|
| 330 |
1.1 |
|
| 331 |
1.1 |
|
| 332 |
1.1 |
|
| 333 |
1.1 |
|
| 334 |
1.1 |
|
| 335 |
1.1 |
|
| 336 |
1.1 |
|
| 337 |
1.1 |
|
| 338 |
1.1 |
|
| 339 |
1.1 |
|
| 340 |
1.1 |
|
| 341 |
1.1 |
|
| 342 |
1.1 |
|
| 343 |
1.1 |
|
| 344 |
1.1 |
|
| 345 |
1.1 |
|
| 346 |
1.1 |
|
| 347 |
1.1 |
|
| 348 |
1.1 |
|
| 349 |
1.1 |
|
| 350 |
1.1 |
|
| 351 |
1.1 |
|
| 352 |
1.1 |
|
| 353 |
1.1 |
|
| 354 |
1.1 |
|
| 355 |
1.1 |
|
| 356 |
1.1 |
|
| 357 |
1.1 |
|
| 359 |
1.1 |
|
| 365 |
1.1 |
|
| 367 |
1.1 |
|
| 370 |
1.1 |
|
| 372 |
1.1 |
|
| 375 |
1.1 2.2 |
|
| 376 |
1.1 |