I saw several similar questions but non with an answer that worked for me. I am working on converting an existing project over to use Hibernate. I am using Mysql for the database. I have a class like this:
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@ManyToOne
@JoinColumn(name = "group_id_user")
private Group group;
@Column(name = "name")
private String name;
...
// getters and setters....
}
and a class like this:
@Entity
@Table(name = "group")
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "group_id")
private Long groupId;
@Column(name="group_name")
private String groupName;
...
// getters and setters....
}
When I fetch a User
item from the database I convert it to JSON and end up with a result in this format:
{
"user": {
"userId": 1,
"group": {
"groupId": 3,
"groupName": "Group Three",
},
"name": "John Doe",
}
}
In my case, the Group
object is actually quite a bit bigger than in this example and also contains references to another table as well (i.e. Group
has a SomeOtherClass
object). I want to end up with something that looks like the following:
{
"user": {
"userId": 1,
"groupId": 3,
"name": "John Doe",
}
}
Is there a way to just get the groupId in my JSON response instead of the entire Group
object?
I have tried adding FetchType.Lazy to my User class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id_user")
private Group group;
but when I try to convert the User
object to Json I get the following error:
error: Errorjava.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
I found this answer which gets rid of the error, but then the JSON object is exactly the same as it was before I added the FetchType.LAZY
. It looks like somehow converting to JSON adds in the values that the FetchType.LAZY
was ignoring.
I am using converting to JSON as follows:
GsonBuilder b = new GsonBuilder();
Gson gson = b.create();
//b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY);
com.google.gson.JsonArray jsonArray = gson.toJsonTree(out).getAsJsonArray();
Correct me if I wrong, as I'm new to hibernate. Based on my experience so far, Yes it is possible to send only object ID instead of sending the whole object details in response. By using @JsonIdentityReference
and @JsonIdentityInfo
find documentation here
Below is the sample code worked for me:
Group Class
@Entity
@Table(name = "group")
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "group_id")
private Long groupId;
@Column(name="group_name")
private String groupName;
...
// getters and setters....
}
User Class
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@ManyToOne
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "group_id")
@JsonIdentityReference(alwaysAsId = true)
@JoinColumn(name = "group_id_user")
private Group group;
...
// getters and setters....
}
Result is
{ "userId": 1, "group": 1, "name": "Sample User" }
I used below code to convert JSON
ObjectMapper mapper = new ObjectMapper();
try {
json = mapper.writeValue(user);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
I found the solution for my issue here
Option 1: (from @JBNizet's comment)
Create classes (DTOs) which represent the output of your API, transform your entities to instances of these DTO classes, and use Gson to serialize the DTOs to JSON. Good REST frameworks do the last part automatically for you.
For this example you could create a UserItem class that looked like this:
public class UserItem {
private Long userId;
private Long groupId;
private String name;
...
// getters and setters....
}
Then you would need a method that converts the User entity to a UserItem:
public UserItem UserToUserItem(User user) {
UserItem userItem = new UserItem();
userItem.setUserId(user.getUserId());
userItem.setGroupId(user.getGroup().getGroupId());
userItem.setName(user.getName());
return userItem;
}
The use the userItem in your response.
Option 2:
UPDATE: Updated option 2 to something that works
If you don't want to write DTOs for each entity class because (like this example) the only field that needs changed is the Group object to the groupId you can create an ExclusionStrategy
and use the @Expose
annotation as follows:
First create a class that implements ExclusionStrategy
public class SerializeExclusionStrat implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
final Expose expose = fieldAttributes.getAnnotation(Expose.class);
return expose != null && !expose.serialize();
}
@Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
}
@Entity
@Table(name = "user")
public class User {
Then add the @Expose
annotation to User class. Adding the additional field groupId
will allow the groupId to be returned in your queries and in your json responses. Just make sure to mark groupId with updatable=false
and insertable=false
.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@ManyToOne
@JoinColumn(name = "group_id_user")
@Expose(serialize = false)
private Group group;
@Column(name = "group_id_user", updatable = false, insertable = false)
private Long groupId;
@Column(name = "name")
private String name;
...
// getters and setters....
}
Then set the Exclusion strategy when creating the Gson:
GsonBuilder b = new GsonBuilder();
b.serializeNulls();
b.setExclusionStrategies(new SerializeExclusionStrat());
Gson gson = b.create();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With