Type annotation of Hibernate is not working - spring

I want to convert UUID value from bytes to string by using #Type annotation as:
#Type(type="uuid-char") but I am getting the error: The attribute type is undefined for the annotation type Type.
Import: import org.hibernate.annotations.Type;
Code:
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", columnDefinition = "VARCHAR(40)")
#Type(type="uuid-char")
#JsonIgnore
private UUID id;
I also searched it here https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e2794 but it had the same syntax I was using. What am I missing?

Try use #JdbcTypeCode(SqlTypes.VARCHAR) instead.
After updating to Spring Boot 3.0.0 we had the same problem. Using #JdbcTypeCode(SqlTypes.VARCHAR) instead. I tried it out with PSQL.

Tried a workaround to save the UUID string to the database. I changed the data type of the id field from UUID to String and it worked fine.
Updated entity class definition:
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", columnDefinition = "VARCHAR(40)")
#JsonIgnore
private String id;

Related

Spring data rest ManyToMany mapping PUT/update operation is not replacing the nested object

I started to learn spring data rest. I'm doing PUT operation and it's not working for the nested objects for ManyToMany relationship, whereas it works fine for OneToMany relation.
Entities structures:
#Table(name="CONFIG_DTLS",schema = "app_txn")
#Entity
public class Config {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "NAME", nullable = false, length = 75)
private String name;
/*Unable to replace the data in the MBR_CONFIG_MAPPING table in the put operation.
When the control comes to #HandleBeforeSave annotated method in PUT operation,
the request data contains the existing Member info instead of the one which i'm passing in the PUT request body */
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.EAGER)
#JoinTable(schema = "app_txn", name = "MBR_CONFIG_MAPPING",
joinColumns ={#JoinColumn(name="CONFIG_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name="MBR_ID",referencedColumnName = "ID")}
)
private Set<Member> members;
//able to replace the notifications completely in PUT operation
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "CONFIG_ID",referencedColumnName = "ID")
private Set<Notification> notifications;
}
Member.java
#Table(name="MBR_DTLS",schema = "app_txn")
#Entity
public class Member {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "OTHER_MBR_DATA", updatable = false)
private String otherMbrData;
}
Notification.java
#Table(name="NOTIFICATIONS",schema = "app_txn")
#Entity
public class Notification {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name="LEVEL")
private String level;
#Column(name="EMAIL")
private String email;
}
Interfaces:
#RepositoryRestResource(collectionResourceRel = "configs", path="configs")
public interface ConfigRepo extends PagingAndSortingRepository<Config,UUID> {
}
#RepositoryRestResource(exported=false) // don't want to users to manipulate it directly.
public interface MemberRepo extends PagingAndSortingRepository<Member,Object> {
}
Here I don't want to add or modify anything in the MBR_DTLS table as it is loaded by another backend process. I want to update only the mapping details MBR_CONFIG_MAPPING table whenever user does the PUT/update operation. POST/create operation is working fine. Please share your thoughts on how to fix this and if you have any questions add it in the comment section.
PS: I referred some links online but that does not help much - Spring Data REST - PUT request does not work properly since v.2.5.7

JPA repository findBy foreign key for ManyToMany relationship

I have the following relationship in the data base
Database Diagram
And these are the entities
Apartment.java
#Entity
#Table(name = "apartment")
#Data
public class Apartment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_apartment")
private long id;
#ManyToMany
#JoinTable(
name = "apartment_facility",
joinColumns = #JoinColumn(name = "id_apartment"),
inverseJoinColumns = #JoinColumn(name = "id_facility"))
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
}
Facility.java
public class Facility {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_facility")
private long id;
#Column(name = "name")
private String name;
}
My question is how can I create a findBy method in JpaRepository in order to find all Apartments having the required facilities
I'm thinking of the result of this request:
api/apartments/findByFacilities?facilities=gym,pool,parking
I have tried to do a query but couldn't quite get it. Also tried using Jpa as following
List<Apartment> findByFacilities(#RequestParam("facilities") List<String> facilities);
But I'm getting the following error. Is there a walkaround?
Failed to convert from type [java.lang.String] to type [java.lang.Long]
In your first code block List <Facility> facilities = new ArrayList<>(); I guess you forgot to add the code line.
When I look at your link below, I understand that you want to search by the name of the facility.
api/apartments/findByFacilities?facilities=gym,pool,parking
For this, your code in Repository should be as follows:
List<Apartment> findDistinctByFacilitiesNameIn(List<String> facilities);
If we don't add distinct supported keyword here, each record repeats as many as the facilities it contains.
If you want to search differently like, not like, etc., See the supported keywords inside method names table here: Query Creation document.

Foreign key constraint #ManyToMany relationship preventing deletion

I've three associated records (Conference, SubmissionRecord, SubmissionAuthorRecord). Every SubmissionRecord has a Conference object and has a List<SubmissionAuthorRecord>.
When I delete a Conference record if the SubmissionRecord is associated with that Conference, it should cascade and delete as well. However, I keep getting a java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`viz`.`submission_record_author_set`, CONSTRAINT `FKgnqq52l26bitkmojk1oiuaki1`
FOREIGN KEY (`submission_record_s_id`) REFERENCES `submission_record` (`s_id`)) error message.
The table submission_record_author_set is create automatically and I have no entity that maps to it.
I understand the issue lies in the fact that the submission_record_author_set rows are preventing the SubmissionRecord from being deleted and have tried the #PreRemove method described here (How to remove entity with ManyToMany relationship in JPA (and corresponding join table rows)?) but to no avail. Maybe there's an issue with the ManyToMany annotation? Cause I do not see the equivalent annotation in the SubmissionAuthorRecord either.
#Entity
public class SubmissionRecord {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
#Column(name = "s_id")
private Long id;
#Exportable(name = "Submission Id", nameInDB = "s_submission_id")
#Column(name = "s_submission_id")
private String submissionId;
// internal set of authors of the associated
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonIgnore
private List<SubmissionAuthorRecord> authorSet;
#JoinColumn(name="conference_id")
#ManyToOne(fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
private Conference conference;
//...
}
#Entity
public class Conference {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String creatorIdentifier;
private String conferenceName;
private String conferenceYear;
}
#Entity
public class SubmissionAuthorRecord {
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "xyz")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#JsonSerialize(using = ToStringSerializer.class)
#Column(name = "s_author_id")
private Long id;
private String dataSet;
#Column(name = "s_author_name")
private String name;
}
The submission_author_record_set table looks like the following:

Set of enum Spring data

I want to use in my spring application with spring data set of enums, which will be stored in db. Currently i tried it in that way:
#NotNull
#Column(name = "ROLES")
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = Role.class)
private Role role;
#NotNull
#Column(name = "PERMISSIONS")
#Enumerated(EnumType.STRING)
#ElementCollection(targetClass = Permission.class)
private Set<Permission> permissions;
but as you propably know it do not work. How can i use enums to be stored in db?
Best regards!
You should add #CollectionTable anotation with specified name and join column.
May this will work for you.
#ElementCollection(targetClass = Permission.class)
#CollectionTable(name = "permissions", joinColumns = #JoinColumn(name = "permission_id"))
#Column(name = "permission", nullable = false)
#Enumerated(EnumType.STRING)
Set<Permission> permission;

Spring Data Rest 2.1.0 Cannot POST or PUT Complex Resource

EDIT: This appears to be happening with PUTs as well.
Using spring-data-rest-webmvc version 2.1.0.BUILD-SNAPSHOT I have found that I am unable to POST a resource with a relation pointing to an already existing resource. I have 2 such entities which require references to be instantiated and POSTing to either of their endpoints results in the behavior below.
POSTing a resource without required references works well.
I did a bit of digging and it appears that PersistentEntityResourceHandlerMethodArgumentResolver finds the MappingJackson2HttpMessageConverter just fine, but it ends up throwing an exception while checking whether the ObjectMapper can deserialize the type. The cause of the exception is a NullPointerException.
example POST w/ relations to /reservations:
{
"description" : "test_post",
"dateMade" : "2014-03-03T08:04:44.293-0600",
"timeLastChanged" : null,
"userLastChanged" : null,
"courseTypeId" : null,
"numCredits" : null,
"instructor" : null,
"numParticipants" : null,
"reservationType" : "MCU",
"status" : "REQUESTED",
"abstract" : null,
"requestor" : "http://localhost:8080/users/2",
"submitter" : "http://localhost:8080/users/2",
"conferences" : []
}
RESPONSE:
{
cause: null
message: "No suitable HttpMessageConverter found to read request body into object of type class domain.Reservation from request with content type of application/json!"
}
POST w/ no relations to /roomGroups:
{
"description" : "All Rooms",
"isOffNetwork" : false,
"roomGroupType" : "STANDARD"
}
RESPONSE:
201 Created
Is there something wrong about the JSON I am POSTing which is resulting in an NPE from the ObjectMapper? Is there a workaround of some kind? This was working for me in 2.0.0.RC1 using a slightly different scheme for including reference links in the JSON and since the version of the Jackson dependencies appears to have stayed the same I wonder what is causing this issue...
Thanks for any help!
UPDATE:
This issue now seems un-related to the associated entities...
I created a new #Entity ConnectionRequest as follows:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CONNECTION_REQUEST_ID")
private Long id;
#Column(name = "FROM_ENTITY_ID", nullable = false)
private Long fromId;
#Column(name = "TO_ENTITY_ID", nullable = false)
private Long toId;
#Convert(converter = EntityTypeConverter.class)
#Column(name = "FROM_ENTITY_TYPE_ID", nullable = false)
private EntityType fromType;
#Convert(converter = EntityTypeConverter.class)
#Column(name = "TO_ENTITY_TYPE_ID", nullable = false)
private EntityType toType;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE})
#JoinColumn(name = "CONFERENCE_ID", nullable = false)
private Conference conference;
I can POST a new ConnectionRequest record with a Conference relation included in the json as such {"conference" : ".../conferences/1"}.
I am however still getting the same exception w/ this #Entity Reservation:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "RESERVATION_ID")
private Long id;
#Column(name = "DESCRIPTION", length = 50, nullable = false)
private String description;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DATE_MADE", nullable = false)
private Date dateMade;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "TIME_LAST_CHANGED")
private Date timeLastChanged;
#Column(name = "USER_LAST_CHANGED")
private Integer userLastChanged; // TODO why is this an int?
#Column(name = "ABSTRACT", length = 2000)
private String _abstract;
#Column(name = "COURSE_TYPE_ID")
private Integer courseTypeId;
#Column(name = "NUMBER_OF_CREDITS")
private Integer numCredits;
#Column(name = "INSTRUCTOR", length = 255)
private String instructor;
#Column(name = "NUMBER_OF_PARTICIPANTS")
private Integer numParticipants;
#Convert(converter = ReservationTypeConverter.class)
#Column(name = "RESERVATION_TYPE_ID", nullable = false)
private ReservationType reservationType;
#Convert(converter = StatusConverter.class)
#Column(name = "STATUS_ID")
private Status status;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE})
#JoinColumn(name="REQUESTOR_USER_ID", nullable=false)
private User requestor;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE})
#JoinColumn(name="SUBMITTER_USER_ID", nullable=false)
private User submitter;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "reservation", cascade = {CascadeType.REMOVE})
private Set<Conference> conferences = new HashSet<>();
I'm not sure what's special about this class that's causing things to go awry...
The issue was the following:
Both of the non-postable entities had a property called _abstract due to it being a reserved word in Java. I had named the getter and setter for this property getAbstract() and setAbstract() respectively.
Jackson appears to have been throwing a null pointer exception since the getter and setter did not match the property name as expected.
When I changed the property name to resvAbstract and updated the accessors to getResvAbstract() and setResvAbstract() everything came together and started working.
I'm still curious about the change that led to this issue showing up, but I'm glad it's working!

Resources