Spring Jackson serialization by interface - java

I have two interfaces and one class:
#JsonDeserialize(as = UserEvent.class)
#JsonSerialize(as = EventAttendee.class)
public interface EventAttendee {
Long getId();
void setId(Long id);
User getUser();
void setUser(User user);
UserResponse getUserResponse();
void setUserResponse(UserResponse userResponse);
}
#JsonDeserialize(as = UserEvent.class)
#JsonSerialize(as = UserAttendee.class)
public interface UserAttendee {
Long getId();
void setId(Long id);
Event getEvent();
void setEvent(Event user);
UserResponse getUserResponse();
void setUserResponse(UserResponse userResponse);
}
public class UserEvent extends BaseEntity implements EventAttendee, UserAttendee {
private Event event = new Event();
private User user = new User();
private UserResponse userResponse;
}
I want return different values of UserEvent based on interface I returning from my controller. Like this:
public List<EventAttendee> getEventAttendees(#PathVariable Long eventId) {
}
public List<UserAttendee> getUserEvents(#PathVariable Long userId) {
}
But it taking first implemented interface(in my case EventAttendee) and return it type from BOTH controlers.
How can I return EventAttendee values from one controller, and UserAttendee from another?

Ok, after I posted question I found answer...
I used Views instead of interfaces. There still interfaces in class, but it's serve for other needs now (not for Jackson).
public class Views {
public interface UserResponse {}
public interface Event extends UserResponse {}
public interface User extends UserResponse {}
}
public class UserEvent extends BaseEntity implements EventAttendee, UserAttendee {
#JsonView(Views.User.class)
private Event event = new Event();
#JsonView(Views.Event.class)
private User user = new User();
#JsonView(Views.UserResponse.class)
private UserResponse userResponse;
}
#JsonView(Views.Event.class)
public List<EventAttendee> getEventAttendees(#PathVariable Long eventId) {
}
#JsonView(Views.User.class)
public List<UserAttendee> getUserEvents(#PathVariable Long userId) {
}

Related

MongoRepository findAll() returns empty list

findAll() of mongoRepository returns empty list. what is wrong with the below code?
API used for counting the number of documents in the collection works fine.
Controller
#RestController
#RequestMapping("/api/api-management/scopes")
public class AuthScopesController {
private final ScopesService scopesService;
#Autowired
AuthScopesController(ScopesService scopesService) {
this.scopesService = scopesService;
}
#PostMapping("/")
public AuthScope createScope(#RequestBody AuthScope authScope) {
return scopesService.createAuthScope(authScope);
}
#GetMapping("/")
public List<AuthScope> getAllScopes() {
return scopesService.getAuthScopes();
}
}
service
#Service
public class ScopesService {
private final AuthScopeRepository authScopeRepository;
public ScopesService(AuthScopeRepository authScopeRepository) {
this.authScopeRepository = authScopeRepository;
}
public AuthScope createAuthScope(AuthScope authScope) {
return authScopeRepository.save(authScope);
}
//TODO: recheck
public List<AuthScope> getAuthScopes() {
return authScopeRepository.findAll();
}
}
repository
#Repository
public interface AuthScopeRepository extends MongoRepository<AuthScope, String> {
Optional<AuthScope> findByScope(String id);
}
model is as follows
#Data
#Document("auth-scopes")
public class AuthScope {
#Id
private String scope;
private String belongsToApi;
private String belongsToApiTitle;
private String description;
}
found the issue. in order to findAll() to work, the model has to have deleted status.
I've updated the model as follows
#Data
#Document("auth-scopes")
public class AuthScope {
#Id
private String scope;
private String belongsToApi;
private String belongsToApiTitle;
private String description;
private boolean deleted;
}

Is it possible in Hibernate to override entity field name in a child entity?

I'm writing an app using Spring Boot, Hiberane and Spring Data.
I have two tables in the db: tableA and tableB.
They have some common fields but their id's,name's are different, also I've created a basic model for them to contain some common fields, right now it looks something like this:
// BaseModel
#MappedSuperclass
public abstract class BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="common_field_1")
private String commonField1;
#Column(name="common_field_2")
private String commonField2;
#Column(name="common_field_3")
private String commonField3;
}
// ExactModel 1
#Entity
#Table(name="table1" ,schema="schema")
public class ExactModel1 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_1", nullable=false)
private long exactModel1Id;
private String exactField1;
}
// ExactModel 2
#Entity
#Table(name="table2" ,schema="schema")
public class ExactModel2 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_2", nullable=false)
private long exactModel2Id;
private String exactField2;
}
And I have some generic logic which implements some general crud logic which works for classes which extend BaseModel:
public abstract class BaseServiceImpl<M extends BaseModel, R extends BaseRepository<M>> implements BaseService<M, Long> {
private final R repository;
public BaseServiceImpl(R repository) {
this.repository = repository;
}
#Override
public M save(M model) {
return repository.save(model);
}
#Override
public List<M> saveAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public M findById(Long id) {
return repository.getOne(id);
}
#Override
public List<M> findAllById(List<Long> ids) {
return repository.findAllById(ids);
}
#Override
public List<M> findAll() {
return repository.findAll();
}
#Override
public M update(M model) {
return repository.save(model);
}
#Override
public List<M> updateAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public void delete(M model) {
repository.delete(model);
}
#Override
public void delteById(Long id) {
repository.deleteById(id);
}
#Override
public void deleteInBatch(List<M> models) {
repository.deleteInBatch(models);
}
#Override
public Long countModels() {
return repository.count();
}
}
The thing is now I need to get somehow the id of the entity I work with in this generic logic, but there is no id field in BaseModel, so I can't just use baseModel.getId().
The question: is it possible to define a mock id field in BaseModel and override it in the child classes, so I can use this id in the generic methods but Hibernate fills the actual ids on the runtime for me?

jackson deserialization with 2 fields self referencing the same class (self-reference cycle)

I have below class referencing to itself:
#Entity
#Inheritance(strategy = TABLE_PER_CLASS)
//#JsonIdentityInfo(property="rowId", generator = ObjectIdGenerators.PropertyGenerator.class)
public abstract class AbstractEntity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 568799551343430329L;
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(name="createdBy")
protected User createdBy;
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(name="lastUpdatedBy")
protected User lastUpdatedBy;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(unique = true, nullable = false, updatable = false, length = 7)
private Integer rowId;
public User getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
public User getLastUpdatedBy() {
return this.lastUpdatedBy;
}
public void setLastUpdatedBy(User lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
public Integer getRowId() {
return this.rowId;
}
public void setRowId(Integer RowId) {
this.rowId = RowId;
}
public String toString() {
return "[Id]:" + this.rowId + " - [CreatedBy]:" + this.createdBy;
}
}
Then I have a class User extending this class and a RepositoryUser interface:
public interface RepositoryUser extends CrudRepository<User, Integer> {
}
And a Controller:
#Controller
#RequestMapping(path = "/user")
public class ServiceUser {
#Autowired
private RepositoryUser repositoryUser;
#GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
public #ResponseBody Iterable<User> getAllUsers() {
return repositoryUser.findAll();
}
#PostMapping(path="/add", consumes="application/json")
public #ResponseBody User createOneUser(#RequestBody User user) {
System.out.println(user);
return repositoryUser.save(user);
}
}
My issue is that I'm making reference to User twice (createdby and lastupdatedby) in the same class and either I tried JSonIdentityInfo, Jsonmanaged,jsonback nothing works. correctly.
I need to be able to have
{
User 1 data including created by and last updated by
User 2 data including created by and last updated by
}
and when I add I need to set the user who creates the record.
Can you please help me ?
Thanks a lot!
You could write/try a Custom Serializer Using StdSerializer.
Example of CustomJsonSerializer. NB: Did not run the code.
public class CustomJsonSerializer extends StdSerializer<AbstractEntity> {
public CustomJsonSerializer() {
this(null);
}
public CustomJsonSerializer(Class<AbstractEntity> t) {
super(t);
}
#Override
public void serialize(AbstractEntity value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
Field[] fields = value.getClass().getDeclaredFields();
jgen.writeStartObject();
for (Field field : fields) {
field.setAccessible(true);
try {
// Do the proper field mapping for field types . Object type example
jgen.writeObjectField(field.getName(), field.get(value));
} catch (Exception e) {
// catch error
}
}
jgen.writeEndObject();
}
}
Then on your Rest Method use #JsonSerialize
#JsonSerialize(using = CustomJsonSerializer.class)
#GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
public #ResponseBody Iterable<User> getAllUsers() {
return repositoryUser.findAll();
}
Please see Custom Serializer And StdSerializer
Possible different solution
jackson-bidirectional infinite-recursion

Get data from a repository using Spring

Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.

Mongo collection name with spring data not set correctly

I'm trying to use a base class for my mongo collections and then have the collection name come from the derived classes; however, the collection name is always just entity ( instead of, in this case, Derived).
I have the following abstract class:
public abstract class Entity {
#Id
private String id;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
}
And derived classes like:
// I've also tried #TypeAlias("Derived")
#Document(collection = "Derived")
public class Derived extends Entity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
With a repository like:
#Component
public interface Repository<T extends Entity> extends MongoRepository<T, String> {
T findById(String id);
}
Follow this working example.
Let's declare a base document model from which every document model must inherit
import java.math.BigInteger;
import org.springframework.data.annotation.Id;
public class BaseDocument {
#Id
private BigInteger id;
public BigInteger getId() {
return id;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {
return false;
}
BaseDocument that = (BaseDocument) obj;
return this.id.equals(that.getId());
}
#Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}
Just for example, I declare my Code model. Note that collection name, if differs from class name, can be set with #Document annotation
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
#Document(collection = "codes")
public class Code extends BaseDocument {
#Field("code")
private String code;
#Field("holiday")
private String holiday;
public Code(String code, String holiday) {
super();
this.code = code;
this.holiday = holiday;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getHoliday() {
return holiday;
}
public void setHoliday(String holiday) {
this.holiday = holiday;
}
#Override
public String toString() {
return "Code [code=" + code + ", holiday=" + holiday + "]";
}
}
At this point you can easily use these classes using MongoOperations and other classes in order to operate on database and collections. MongoOperations has all the methods you need to operate on collections such as: findOne, findAll and so on.
Just to be clear, you can use MongoOperations in the following way:
MongoOperations mongoOps = new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "yourDBname"));
I strongly suggest to implement a service class which operate on collection, like this one, instead of operating directly on it:
import java.util.List;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
public class CodesService extends BaseService implements BaseOperations{
#Override
public BaseDocument findOne(Long id) {
Query query = new Query(Criteria.where("code").is("8"));
return this.mongoOps.findOne(query, Code.class);
}
#Override
public List<? extends BaseDocument> findAll() {
List<Code> subs = this.mongoOps.findAll(Code.class);
List<? extends BaseDocument> codes = subs;
return codes;
}
}
This class implements the following interface:
import java.util.List;
public interface BaseOperations {
public BaseDocument findOne(Long id);
public List<? extends BaseDocument> findAll();
}
Hope this may help.
Try to implement the Repository<T extends Entity> interface like given below
public interface SomeInterfaceName extends Repository<Derived>{
}
Now try to #Autowire the Repository<Derived> repo
It will give you the desired output

Categories

Resources