This is a continuation of my previous question posted
I have a Student object marked as a entity, this student object is a value called assignments which is a Map<Integer, AssignmentList> assignments the AssignmentList is a custom data object for storing the list of assignments among other data associated with that list. the AssignmentList is not an annotated with a @entity or @data (code below). however in the object a list assignments are stored these assignments objects are marked as @Entity. Right now i’m still trying to figure out how to properly use the attribute converter so I will also include snippets of my Teacher and Student objects as well as other variatrions of converters i’ve tried.
Entity objects
@Entity
@Table(name = "teachers")
@Data
@EqualsAndHashCode
@ToString
@Getter
@Setter
public class Teacher implements UserDetails {
// other values
@ElementCollection
@Convert(converter = StudentListMapConverter.class)
@MapKeyColumn(name = "period")
private Map<Integer, List<Student>> students;
}
@Entity
@Table(name="students")
@Data
@EqualsAndHashCode
@ToString
@Getter
@Setter
public class Student implements UserDetails {
// other values
@ElementCollection
@CollectionTable(name = "course_assignment_list", joinColumns = @JoinColumn(name = "course_id"))
@MapKeyColumn(name = "period")
@Column(name = "assignment_list", columnDefinition = "TEXT")
@Convert(converter = AssignmentListConverter.class)
private Map<Integer, AssignmentList> assignments;
// custom constructors
}
@Entity
@Table(name = "assignments")
@Data
@EqualsAndHashCode
@ToString
@Getter
@Setter
@NoArgsConstructor
public class Assignment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "assignment_id")
private Long id;
private String name;
private Double grade;
private boolean completed;
private boolean overdue;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate dueDate;
private String assignmentType;
AssignmentList object
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AssignmentList {
String subject;
double averageGrade;
List<Assignment> assignments;
}
Converters
I’ll put the most recent convert I’ve tried first followed by the rest.
@Converter
public class AssignmentListConverter implements AttributeConverter<AssignmentList, String> {
private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
@Override
public String convertToDatabaseColumn(AssignmentList assignmentList) {
try {
return objectMapper.writeValueAsString(assignmentList);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting AssignmentList to JSON", e);
}
}
@Override
public AssignmentList convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, AssignmentList.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting JSON to AssignmentList", e);
}
}
}
@Converter
public class StudentListMapConverter implements AttributeConverter<Map<Integer, List<Student>>, String> {
private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
private static final TypeFactory typeFactory = objectMapper.getTypeFactory();
@Override
public String convertToDatabaseColumn(Map<Integer, List<Student>> attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting Map<Integer, List<Student>> to JSON", e);
}
}
@Override
public Map<Integer, List<Student>> convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, typeFactory.constructMapType(Map.class, typeFactory.constructType(Integer.class), typeFactory.constructCollectionType(List.class, Student.class)));
} catch (IOException e) {
throw new IllegalArgumentException("Error converting JSON to Map<Integer, List<Student>>", e);
}
}
}
Here are the other variations I tried.
@Converter
public class AssignmentListMapConverter implements AttributeConverter<Map<Integer, AssignmentList>, String> {
private static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
private static final TypeFactory typeFactory = objectMapper.getTypeFactory();
@Override
public String convertToDatabaseColumn(Map<Integer, AssignmentList> attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting Map<Integer, AssignmentList> to JSON", e);
}
}
@Override
public Map<Integer, AssignmentList> convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, typeFactory.constructMapType(Map.class,
typeFactory.constructType(Integer.class),
typeFactory.constructType(AssignmentList.class)));
} catch (IOException e) {
throw new IllegalArgumentException("Error converting JSON to Map<Integer, AssignmentList>", e);
}
}
}
@Converter
public class AssignmentConverter implements AttributeConverter<Object, String> {
private static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
private static final TypeFactory typeFactory = objectMapper.getTypeFactory();
@Override
public String convertToDatabaseColumn(Object attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting object to JSON", e);
}
}
@Override
public Object convertToEntityAttribute(String dbData) {
if (dbData.startsWith("{")) {
try {
return objectMapper.readValue(dbData, typeFactory
.constructMapType(Map.class,
typeFactory.constructType(Integer.class),
typeFactory.constructType(AssignmentList.class)));
} catch (IOException e) {
throw new IllegalArgumentException("Error converting JSON to Map<Integer, AssignmentList>", e);
}
} else {
try {
return objectMapper.readValue(dbData, AssignmentList.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error converting JSON to AssignmentList", e);
}
}
}
}
Errors
using the most recent build I get these logs when loading the command line runner method which creates a very basic Teacher, student, and Assignment object for testing:
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:772) ~[spring-boot-3.0.4.jar:3.0.4]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:753) ~[spring-boot-3.0.4.jar:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:317) ~[spring-boot-3.0.4.jar:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[spring-boot-3.0.4.jar:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[spring-boot-3.0.4.jar:3.0.4]
at com.tracer.StudentTrackerApiApplication.main(StudentTrackerApiApplication.java:29) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-3.0.4.jar:3.0.4]
Caused by: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:570) ~[spring-orm-6.0.6.jar:6.0.6]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:659) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:409) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:163) ~[spring-data-jpa-3.0.3.jar:3.0.3]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.6.jar:6.0.6]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218) ~[spring-aop-6.0.6.jar:6.0.6]
at jdk.proxy4/jdk.proxy4.$Proxy150.save(Unknown Source) ~[na:na]
at com.tracer.StudentTrackerApiApplication.lambda$run$0(StudentTrackerApiApplication.java:72) ~[classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:769) ~[spring-boot-3.0.4.jar:3.0.4]
… 10 common frames omitted
Caused by: jakarta.persistence.RollbackException: Error while committing the transaction
at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:83) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:561) ~[spring-orm-6.0.6.jar:6.0.6]
… 26 common frames omitted
Caused by: java.lang.UnsupportedOperationException: null
at org.hibernate.type.descriptor.java.spi.CollectionJavaType.unwrap(CollectionJavaType.java:82) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType.unwrap(BasicCollectionJavaType.java:333) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType.unwrap(BasicCollectionJavaType.java:48) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.descriptor.jdbc.ArrayJdbcType$2.getArray(ArrayJdbcType.java:176) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.descriptor.jdbc.ArrayJdbcType$2.doBind(ArrayJdbcType.java:122) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.descriptor.jdbc.BasicBinder.bind(BasicBinder.java:63) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:221) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:217) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:34) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:1010) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1473) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:47) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:612) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:483) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) ~[na:na]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:480) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:329) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1425) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:477) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2234) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1930) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
… 27 common frames omitted
when messing around with the data loader I’ve found that when I don’t set the Student List in Teacher to the created students or assignment objects. or when i comment out the creation of the student and Assignment objects I don’t get any error when booting up the application. But thats all I’ve really managed to deduce.
Andrew Lam is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.