I have a service wherein I use entities with following structure
#Entity
public class Product {
#Id
private Integer id;
private String productId;
private List<MobileCompany<?>> companies;
// ...
}
public class MobileCompany<T> extends Company<T> {
private static final long serialVersionUID = -4809948191835736752
private String simType;
// ....
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(name= "samsung", value = Samsung.class),
#JsonSubTypes.Type(name= "htc",value = Htc.class)})
public class Company<T> implements Serializable {
private static final long serialVersionUID = -8869676577723436716L;
private T info;
private String type;
// ...
}
My service looks similar to below
Product sample(#RequestBody Product product) {
....// not doing any update on product
return product;
}
with request as below ..
{
id:0903673902783,
companies:[
{
simType:"DUAL",
type:"samsung"
}
]
}
and get back below response
{
id:0903673902783,
companies:[
{
simType:"DUAL",
type:"MobileCompany"
}
]
}
Why am I getting type as "MobileCompany" instead of "samsung" ?
Note : "MobileCompany" is the name of the class which extends Company and in the response I am getting "MobileCompany" as type instead of the type specified in the request. Product is having list of MobileCompany.
Related
I have my API which takes a FileImportInput and this has the following model
FileImportInput {
private String importType;
private String resourceUri;
#JsonProperty("settings")
private MriImportRequest settings;
}
This MriImportRequest is the base class with the following fields
#JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT, property = "settings")
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = MriExcelImportRequest.class, name = "sheets"),
#JsonSubTypes.Type(value = MriTextFileImportRequest.class, name = "files")
})
public class MriImportRequest {
private int folderId;
private String locale;
private String currency;
}
Below are other two classes with the following definitions.
#JsonTypeName("files")
public class MriTextFileImportRequest extends MriImportRequest {
private int accountsFileId;
private int locationsFileId;
private int reinsuranceFileId;
private int mappingFileId;
}
The other class is as follows
#JsonTypeName("sheets")
public class MriExcelImportRequest extends MriImportRequest {
private int accountsSheetIndex;
private int locationsSheetIndex;
private int reinsuranceSheetIndex;
private int mappingFileIndex;}
The request JSON is below, When i make a request, Unrecognized field "files" I am not sure whats missing. The request Json is below
{
"importType": "mri",
"resourceUri": "riskdata/v1/exposuresets/es1/exposures/sdfd-2232-skdj-3434/portfolios/12",
"settings": {
"files": {
"accountsFileId": 5387,
"locationsFileId": 5388,
"reinsuranceFileId": 5389,
"mappingFileId": 5390
},
"folderId": 1686,
"currency": "USD",
"locale": "US"
}
}
The JsonTypeInfo.As#WRAPPER_OBJECT is used for serialization while you are trying to deserialize your json, so no use for it in this case. Instead you can use the JsonTypeInfo.Id#DEDUCTION to deduce types based on the fields available (in this case if files property is available the value will be automatically deserialized to the MriTextFileImportRequest subclass:
#Data
public class FileImportInput {
private String importType;
private String resourceUri;
#JsonProperty("settings")
private MriImportRequest settings;
}
#Data
#JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
#JsonSubTypes({
#JsonSubTypes.Type(value = MriTextFileImportRequest.class)
})
public class MriImportRequest {
private int folderId;
private String locale;
private String currency;
}
#Data
public class MriTextFileImportRequest extends MriImportRequest {
private Files files;
}
#Data
public class Files {
private int accountsFileId;
private int locationsFileId;
private int reinsuranceFileId;
private int mappingFileId;
}
Then the code below prints the json input file:
FileImportInput fileImportInput = mapper.readValue(json, FileImportInput.class);
//ok it prints the input json file
System.out.println(mapper.writeValueAsString(fileImportInput));
I try select data from the table by a filter with Spring Data JPA Specification I think what my implementation is correct, But it doesn't work. Help me please understand my mistake and fix my example.
I have very strange SQL query in log :
select phone0_.id as id1_0_, phone0_.note as note2_0_, phone0_.number as number3_0_, phone0_.operator_login as operator4_0_, phone0_.operator_pass as operator5_0_, phone0_.operator_name as operator6_0_, phone0_.operator_url as operator7_0_, phone0_.reg_date as reg_date8_0_, phone0_.status as status9_0_ from phone phone0_ where 0=1 limit ?
In the end: where 0=1 it's crash my mind. Where did that come from?
Here I fill CriteriaBuilder if filter field not null. I expect to get correctly built Specification object and send it to findAll(Specifications.where(specification), Pageable p) method. But something incorrect.
My repo and specification impl:
public interface PhoneRepository extends CrudRepository<Phone, Integer>, JpaRepository<Phone, Integer>, JpaSpecificationExecutor<Phone> {
class PhoneSpecification implements Specification<Phone> {
private final #NonNull PhoneService.PhoneFilter filter;
public PhoneSpecification(#NonNull PhoneService.PhoneFilter filter) {
this.filter = filter;
}
#Override
public Predicate toPredicate(Root<Phone> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.disjunction();
if (nonNull(filter.getId())) {
cb.disjunction().getExpressions().add(cb.equal(root.get("id"), filter.getId()));
}
if (nonNull(filter.getNote())) {
cb.disjunction().getExpressions().add(cb.like(root.get("note"), filter.getNote()));
}
if (nonNull(filter.getNumber())) {
cb.disjunction().getExpressions().add(cb.like(root.get("number"), filter.getNumber()));
}
if (nonNull(filter.getStatus())) {
cb.disjunction().getExpressions().add(cb.like(root.get("status"), filter.getStatus()));
}
if (nonNull(filter.getOpName())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorName"), filter.getOpName()));
}
if (nonNull(filter.getOpLogin())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorAccLogin"), filter.getOpLogin()));
}
if (nonNull(filter.getOpPassword())) {
cb.disjunction().getExpressions().add(cb.like(root.get("operatorAccPassword"), filter.getOpPassword()));
}
if (nonNull(filter.getRegFrom()) && nonNull(filter.getRegTo())) {
cb.disjunction().getExpressions().add(cb.between(root.get("regDate"), filter.getRegFrom(), filter.getRegTo()));
}
return predicate;
}
}
}
This is service level:
#Service
public class PhoneService {
#Autowired
private PhoneRepository phoneRepository;
public Phone get(int id) {
Phone phone = phoneRepository.findOne(id);
return nonNull(phone) ? phone : new Phone();
}
public Page<Phone> list(#NonNull PhoneFilter filter) {
PhoneSpecification specification = new PhoneSpecification(filter);
return phoneRepository.findAll(Specifications.where(specification), filter.getPageable());
}
#Data
public static class PhoneFilter {
private Pageable pageable;
private Integer id;
private Timestamp regFrom;
private Timestamp regTo;
private String number;
private String opLogin;
private String opPassword;
private String opName;
private String status;
private String note;
}
}
And entity
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "phone")
#ToString(exclude = {"accounts"})
#EqualsAndHashCode(exclude = {"accounts"})
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToMany(mappedBy = "phone", cascade = CascadeType.DETACH)
private Collection<SocialAccount> accounts;
#Column(name = "reg_date")
private Timestamp regDate;
#Column(name = "number")
private String number;
#Column(name = "operator_url")
private String operatorUrl;
#Column(name = "operator_login")
private String operatorAccLogin;
#Column(name = "operator_pass")
private String operatorAccPassword;
#Column(name = "operator_name")
private String operatorName;
#Column(name = "status")
private String status;
#Column(name = "note")
private String note;
}
I find the mistake.
Method CriteriaBuilder.disjunction() this is factory and each time when I call him I got new Predicate object.
This implementation CriteriaBuilderImpl:
public Predicate disjunction() {
return new CompoundPredicate(this, BooleanOperator.OR);
}
Be careful with it.
I have the following setup:
#Entity
#IdClass(MemberAttributePk.class)
public class MemberAttribute {
#Id
#ManyToOne #JoinColumn(name="member_id")
protected Member member;
#Id
protected String name;
private String value;
public MemberAttribute() {}
// get & set
}
And the id class:
public class MemberAttributePk implements Serializable {
protected Member member;
protected String name;
public MemberAttributePk() {}
// get & set
}
I have defined a simple Spring Data repository for MemberAttribute:
#Repository
public interface MemberAttributeRepo extends JpaRepository<MemberAttribute, MemberAttributePk> {
}
Now, all I want to do is persist a member attribute to the database:
public void saveAttribute(Member member, String name, String value) {
MemberAttribute attr = new MemberAttribute(member, name, value);
attributeRepo.save(attr);
}
However, I end up with this server exception:
2016-08-28 00:24:20.673 WARN 5656 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver :
Failed to convert request element: org.springframework.beans.ConversionNotSupportedException:
Failed to convert property value of type [java.lang.Long] to required type [com.example.Member] for property 'member'; nested exception is java.lang.IllegalStateException:
Cannot convert value of type [java.lang.Long] to required type [com.example.Member] for property 'member':
no matching editors or conversion strategy found
Any idea what am I doing wrong?
Thanks!
Simply your code is not JPA compliant. The cause of problem is that you use Member as a part of your PK.
The PK can only be made up of fields of the following Java types
Primitives : boolean , byte , char , int , long , short
java.lang : Boolean , Byte , Character , Integer , Long , Short , String , Enum , StringBuffer
java.math : BigInteger java.sql : Date , Time , Timestamp
java.util : Date , Currency, Locale, TimeZone, UUID
java.net : URI, URL
javax.jdo.spi : PersistenceCapable
This should work:
#Embeddable
public class MemberAttributePk implements Serializable {
#Column(name = "member_id")
protected Long memberId;
#Column(name = "name")
protected String name;
public MemberAttributePk() {}
// get & set
}
#Entity
public class MemberAttribute {
#EmbeddedId
protected MemberAttributePk memberAttributePk;
#ManyToOne
#JoinColumn(name="member_id")
protected Member member;
private String value;
public MemberAttribute() {}
// get & set
}
Or the same with #ClassId
public class MemberAttributePk implements Serializable {
protected Long memberId;
protected String name;
public MemberAttributePk() {}
// get & set
}
#Entity
#IdClass(MemberAttributePk.class)
public class MemberAttribute {
#Id
#Column(name = "member_id")
protected Long memberId;
#Id
#Column(name = "name")
protected String name;
#ManyToOne
#JoinColumn(name="member_id")
protected Member member;
private String value;
public MemberAttribute() {}
// get & set
}
you can try save it using your MemberRepository, because I believe your Member class and MemberAttribute class have a one to many relationship reference, here below is the example
Member class
#Entity
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long id;
#OneToMany(mappedBy = "Member", cascade = CascadeType.ALL)
private Set<MemberAttribute> mMemberAttributes = new HashSet<>();
public void setMemberAttributes(Set<MemberAttribute> mMemberAttributes){
this.mMemberAttributes = mMemberAttributes;
}
public Set<MemberAttribute> getMemberAttributes(){
return mMemberAttributes;
}
// other code
}
MemberRepository class
public interface MemberRepository extends JpaRepository<Member, Long> {
}
code inside your save function
public void saveAttribute(Member member, String name, String value) {
MemberAttribute attr = new MemberAttribute(member, name, value);
member.getMemberAttributes().add(attr);
memberRepository.save(member);
}
I have two simple tables, content and contentType
#Entity
#Table(name = "content")
public class Content implements Serializable {
public Content() {}
public Content(String title, String description) {
this.title = title;
this.description = description;
}
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#ManyToOne
private ContentCategory contentCategory;
#ManyToOne
private ContentType contentType;
// getter/setters
}
#Entity
#Table(name = "contentType")
public class ContentType implements Serializable {
public ContentType() {}
public ContentType(String contentType) {
this.contentType = contentType;
}
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#NotNull
private String contentType;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "contentType")
private Set<Content> content;
`// getter/setters` }
Each content has exactly one type, but many type might be exists in many contents
I am going to retrieve contents with type Book
Here is my repository"
public interface ContentRepository extends JpaRepository<Content, Long> {
Iterable<Content> findByContentType(String contentType);
}
And Here is my test method:
#Test
public void retrieve_content_based_on_type() {
// create and insert a sample content type, i.e. a Book
ContentType contentType1 = new ContentType("Book");
contentTypeRepository.save(contentType1);
//create and insert two contents corresponding to this type
Content cont1 = new Content("t1", "d1");
cont1.setContentType(contentType1);
contentRepository.save(cont1);
Content cont2 = new Content("t2", "d2");
cont2.setContentType(contentType1);
contentRepository.save(cont2);
//retrieve all contents which their type is Book
Iterable<Content> allBooks = contentRepository.findByContentType("Book");
for (Content eachBook : allBooks) {
System.out.println(eachBook);
}
}
I got this exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [Book] did not match expected type [com.aa.bb.domain.ContentType (n/a)];
nested exception is java.lang.IllegalArgumentException: Parameter value [Book] did not match expected type [com.aa.bb.domain.ContentType (n/a)]
You can modify your current method to this:
#Query("select c from Content c where c.contentType.contentType = :contentType")
Iterable<Content> findByContentType(String contentType);
Reason: the contentType in Content entity is of type ContentType whereas in ContentType entity it is of type String
In terms of Spring Data JPA without using query annotation, following is the solution:
Iterable<Content> findByContentTypeContentType(String contentType);
Spring Data Reference Link
Above method is for Repository class ContentRepository.
Try to change:
public interface ContentRepository extends JpaRepository<Content, Long>
{
Iterable<Content> findByContentType(String contentType);
}
To:
public interface ContentRepository extends JpaRepository<ContentType , Long>
{
Iterable<ContentType > findByContentType(String contentType);
}
try changing the variable name to something else in the ContentType class.
private String contentType;
I have a CrudRepository that is supposed to make a query with an array (findByIn). In my repository tests it works, but when I try to use the query in my service, it doesn't work. Could someone explain why it doesn't work? Here is my setup (excluding some code irrelevant to the question)
Database model:
#Entity
#Table(name="Place")
public class Place implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "placeId", nullable = false)
private Long placeId;
#Column(name = "owner", nullable = false)
private String owner;
public Long getPlaceId() {
return placeId;
}
public void setPlaceId(Long placeId) {
this.placeId = placeId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
Repository:
#Repository
public interface PlaceRepository extends CrudRepository<Place, Long> {
List<Place> findByPlaceIdIn(Long[] placeId);
}
Service (this is the part not working):
#Service
public class PlaceService {
#Autowired
private PlaceRepository placeRepository;
public List<Place> getPlaces(Long[] placeIds) {
return placeRepository.findByPlaceIdIn(placeIds);
}
}
The problem is that in my service placeRepository.findByPlaceIdIn(placeIds) returns 0 objects if placeIds contains more than one item. If placeIds contains just one item, the query works fine. I tried replacing return placeRepository.findByPlaceIdIn(placeIds) with this piece of code that does the query for every array item one by one (this actually works, but I'd like to get the query work as it should):
ArrayList<Place> places = new ArrayList<Place>();
for (Long placeId : placeIds) {
Long[] id = {placeId};
places.addAll(placeRepository.findByPlaceIdIn(id));
}
return places;
I know that the repository should work, because I have a working test for it:
public class PlaceRepositoryTest {
#Autowired
private PlaceRepository repository;
private static Place place;
private static Place place2;
private static Place otherUsersPlace;
#Test
public void testPlacesfindByPlaceIdIn() {
place = new Place();
place.setOwner(USER_ID);
place2 = new Place();
place2.setOwner(USER_ID);
place = repository.save(place);
place2 = repository.save(place2);
Long[] ids = {place.getPlaceId(), place2.getPlaceId()};
assertEquals(repository.findByPlaceIdIn(ids).size(), 2);
}
}
I also have another repository for other model, which also uses findByIn and it works fine. I can't see any relevant difference between the repositories. I thought it might offer some more details to show the working repository, so I included it below:
Database model:
#Entity
#Table(name="LocalDatabaseRow")
#JsonIgnoreProperties(ignoreUnknown=false)
public class LocalDatabaseRow implements Serializable {
public LocalDatabaseRow() {}
public LocalDatabaseRow(RowType rowType) {
this.rowType = rowType;
}
public enum RowType {
TYPE1,
TYPE2
};
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
#JsonProperty("id")
private Long id;
#JsonProperty("rowType")
#Column(name = "rowType")
private RowType rowType;
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public RowType getRowType() {
return rowType;
}
public void setRowType(RowType rowType) {
this.rowType = rowType;
}
}
Repository:
#Repository
public interface LocalDatabaseRowRepository extends CrudRepository<LocalDatabaseRow, Long> {
List<LocalDatabaseRow> findByRowTypeAndUserIdIn(RowType type, String[] userId);
}
try using a list instead :
findByPlaceIdIn(List placeIdList);
You have a typo in your code (the repository declaration in the service):
#Autowired
private placeRepository placeRepository;
Should be:
#Autowired
private PlaceRepository placeRepository;