ElementCollection inside Embeddable inside MappedSuperclass - java

I have problems with persisting the following construct with JPA.
#MappedSuperclass
public abstract class SuperFoo {
#Embedded
private Bar bar;
public SuperFoo() {}
public SuperFoo(...) {
...
this.bar = new Bar("barKey");
}
...
public void setBar(Bar bar) {
this.bar = bar;
}
public Bar getBar() {
return bar;
}
}
#Embeddable
public class Bar {
#ElementCollection
Map<String, Long> durations;
#ElementCollection
Map<String, Integer> integers;
public Bar() {}
public Bar(String barKey) {
his.durations = new HashMap<String, Long>();
this.integers = new HashMap<String, Integer>();
durations.put(key, new Long(15));
integers.put(key, new Integer(10));
}
... setters and getters.....
}
#Entity
public class Foo extends SuperFoo {
#Id
private String id;
public Foo() {}
public Foo(String id, *superArguments*) {
super(*superArguments*);
this.id = id;
}
public String getId() { return id; }
public void setId(String id) {
this.id = id;
}
}
#Entity
public class FooContainer {
#Id
String id;
#OneToMany(cascade = CascadeType.ALL)
List<Foo> fooList;
public FooContainer() {}
public FooContainer(String id) {
this.id = id;
this.fooList = new ArrayList<>();
}
public void addFoo(Foo foo) {
this.fooList.add(foo);
}
public void setFooList(List<Foo> fooList) {this.fooList = fooList;}
public List<Foo> getFooList() { return fooList; }
}
Method where persisting takes place:
public void method() {
FooContainer fooContainer = fooContainerRepository.getOne(...);
fooContainer.addFoo(new Foo(...));
fooContainerRepository.save(fooContainer);
}
JPA creates the following tables (looks fine):
foo
foo_durations
foo_integers
Now, when I save an instance of Foo, everything gets persisted (fields in Foo). BUT I get no entries in foo_durations and foo_integers. Furthermore, I get no exceptions.
I did a little research myself and found the following:
"An embeddable class that is contained within an element collection must not contain an element collection."
I think, here, that is not the case because my embeddable class "Bar" is not contained within an element collection. So does anybody know, what I might have done wrong?
Thanks in advance!
Update:
I forgot to mention that the Bar Object inside SuperFoo is not set via constructor but calculated inside the constructor.

Related

Obtaining a nested objects using R2DBC + Webflux

Spring Data R2DBC does not support nested objects, so how correctly retrieve and join nested object to parent object? I have class Foo (child), Bar (Parent), BarDTO:
#Data
public class Foo {
#Id
private String id;
private String value;
}
#Data
public class Bar {
#Id
private String id;
private String fooId;
private String value;
}
#Data
public class BarDTO {
private String id;
private Foo foo;
private String value;
public BarDTO(Bar t2, Foo t1) {
this.id = t2.getId();
this.foo = t1;
this.value = t2.getValue();
}
}
Right now i have this solution, but i am not sure that this is the simplest solution and that i am working correctly with reactive streams:
#AllArgsConstructor
public class BarService {
private final FooRepository fooRepository;
private final BarRepository barRepository;
public Flux<BarDTO> getAllBar() {
return barRepository.findAll()
.collectList()
.flatMapMany(bars -> {
List<Mono<BarDTO>> monoBarList = new ArrayList<>();
for (Bar bar : bars) {
Mono<BarDTO> monoDTO = Mono.just(bar).zipWith(fooRepository.findById(bar.getFooId()))
.flatMap(result -> Mono.just(new BarDTO(result.getT1(), result.getT2())));
monoBarList.add(monoDTO);
}
return Flux.concat(monoBarList);
});
}
}

Jackson: Object identity serializaton/deserialization based on implemented interface

interface Foo
public String key()
class Bar implements Foo
public int id;
public String name;
public Bar2 bar2; <--- bar2.key() should be used as json value
String key() { return name }
class Bar2 implements Foo
public int id;
public int name;
public Bar bar; <--- bar.key() should be used as json value
String key() { return name }
Whenever any object of type Foo is referenced in serialization, it's value should be object.key().
For deserialization, the value to should be used to lookup the actual object (Bar, Bar2, etc)
How can this be done with Jackson?
you need a getter method for the common property. Change Foo into abstract class and define the property and getter method there.
public abstract class Foo implements Serializable{
public String name;
public Foo bar;
public Foo() {
}
public String getBar(){
return bar.name;
}
public void setBar(Foo bar) {
this.bar = bar;
}
}
class Bar extends Foo{
public int id;
public Bar() {
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Bar bar = new Bar();
Bar2 bar2 = new Bar2();
bar.id = 1; bar.name = "bar1";bar.setBar(bar2);
bar2.id = 2; bar2.name = "bar2"; bar2.setBar(bar);
System.out.println(mapper.writeValueAsString(bar));
}
}
public class Bar2 extends Foo {
public int id;
public Bar2() {
}
}

Orika - Map object to null if all fields of object are null

I got 2 classes A and B which both got a company. The company of A has got a little more information than the company of B (Company has an id while CNCompany doesn´t). I want to map all fields using orika. If all fields an object are null, I want the object to be null!
I tried to express this with an unit test. What has to be done to get this running?
public class A {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
public class B {
private CNCompany company;
public CNCompany getCompany() {
return company;
}
public void setCompany(CNCompany company) {
this.company = company;
}
}
public class Company {
private Id id;
private AccountId accountId;
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class CNCompany {
private AccountId accountId
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class MyMapper extends ConfigurableMapper {
#Override
protected void configure(MapperFactory factory) {
factory.classMap(A.class, B.class) //
.mapNulls(false).mapNullsInReverse(false) //
.byDefault() //
.register();
}
}
#Test
public void testMap() throws Exception {
A a = new A();
Company company = new Company();
Id id = new Id();
id.setValue("1");
company.setId(id);
a.setCompany(company);
MyMapper myMapper = new MyMapper();
B outcome = myMapper.map(a, B.class);
assertThat(outcome.getCompany(), is(nullValue()));
}
If I understand correctly you want getCompany to return null if the Company object contains only null values.
In Orika you can control conversion with a custom converter. For your example that might look something like:
public class CompanyConverter extends CustomConverter<Company, CNCompany> {
public CNCompany convert(Company source, Type<? extends CNCompany> destinationType) {
if (isNothingButNulls(source)) {
return null;
}
final CNCompany newCompany = new CNCompany();
// do your thing
return newCompany;
}
}
I've never written a CustomConverter that can return null so I'm not 100% sure this will work but it should. Note that the converter will still need to be registered. The documentation I linked shows how to register depending on what level you want the converter at.

Json Jackson - Using Alias for field names [duplicate]

I use different NoSQL databases and depending on the database I need to name the "id" different. So for example in OrientDB the id is named "#rid"
#JsonProperty("#rid")
private String id;
And for MongoDB the id is named "_id"
#JsonProperty("#_id")
private String id;
I do not know what is wrong with the modern DB developers not just naming the id field "id" ^^. But now I have a problem. How can I dynamically serialize/deserialize the id field in some case as "#rid" and in another case as "_id"?
EDIT:
Based on rmullers suggestion I have tried to use mixins. So I have for example:
public interface IdMixins {
}
public interface MongoIdMixIn extends IdMixins {
#JsonProperty("_id") String getId();
#JsonProperty("_id") void setId(String id);
}
public interface OrientIdMixIn extends IdMixins{
#JsonProperty("#rid") String getId();
#JsonProperty("#rid") void setId(String id);
}
Where IdMixins is a completly empty interface just used to get more controll which interfaces can be passet to the mapper.
Then there is a class:
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#javaClass")
public abstract class AbstractBean implements Serializable {
private static final long serialVersionUID = -1286900676713424199L;
// #JsonProperty("#rid")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
But when I run this simple test, the output is still "id":
public class MixinTest {
public static void main(String[] args) throws JsonProcessingException {
Foo f = new Foo();
f.setId("123");
f.setBar("lala");
ObjectMapper mapper = new ObjectMapper();
ObjectMapper m2 = mapper.copy();
m2.addMixInAnnotations(AbstractBean.class, MongoIdMixIn.class);
System.out.println(m2.writeValueAsString(f));
ObjectMapper m3 = mapper.copy();
m3.addMixInAnnotations(AbstractBean.class, OrientIdMixIn.class);
System.out.println(m3.writeValueAsString(f));
}
public static class Foo extends AbstractBean {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
}
Outputs:
{"#javaClass":"test.MixinTest$Foo","id":"123","bar":"lala","#class":"Foo"}
{"#javaClass":"test.MixinTest$Foo","id":"123","bar":"lala","#class":"Foo"}
Have you tried using http://wiki.fasterxml.com/JacksonMixInAnnotations? Then you can use an OrientDbMixin and a MongoDbMixin with different #JsonProperty configuration.
Update: Working example
public final class JacksonTest {
static final class ExampleBean {
private String id;
private String bar;
#JsonProperty("donotwanttoseethis")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
public interface MongoIdMixIn {
#JsonProperty("_id") String getId();
}
public interface OrientIdMixIn {
#JsonProperty("#rid") String getId();
}
private final static Logger LOG = LoggerFactory.getLogger();
public static void main(String[] args) throws JsonProcessingException {
ExampleBean bean = new ExampleBean();
bean.setId("1234");
bean.setBar("lala");
ObjectMapper m2 = new ObjectMapper();
m2.addMixInAnnotations(ExampleBean.class, MongoIdMixIn.class);
LOG.info(m2.writeValueAsString(bean));
ObjectMapper m3 = new ObjectMapper();
m3.addMixInAnnotations(ExampleBean.class, OrientIdMixIn.class);
LOG.info(m3.writeValueAsString(bean));
}
}

Java: Jaxb Interface

Please take a look at the following example. I got the exception that XmlIDREF annotation is not allowed in class Bar. If you use the concrete Class Bar instead of IBar it works perfectly. But i have to use the interface. Is there a solution for this problem?
#XmlRootElement
public class Foo {
#XmlElement(name = "bar")
public List<Bar> bars;
public String fooProp;
}
public interface IBar {
#XmlID
String getId();
void setId(String id);
}
#XmlRootElement
public class Bar implements IBar {
#XmlIDREF
#XmlAnyElement
public IBar bar;
public String barProp;
private String id;
#Override
#XmlID
public String getId() {
return this.id;
}
#Override
public void setId(String id) {
this.id = id;
}
}
public static void main(String[] args) throws Exception {
File file = null;
file = new File("somewhere");
try(Writer w = new FileWriter(file)){
JAXBContext context = JAXBContext.newInstance("jaxbtest");
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
Foo foo = new Foo();
foo.fooProp ="FooProperty";
Bar bar1 = new Bar();
bar1.barProp = "BarProperty1";
bar1.setId("1");
Bar bar2 = new Bar();
bar2.barProp = "BarProperty2";
bar2.setId("2");
bar1.bar = bar2;
bar2.bar = bar1;
List<Bar> list = new ArrayList<Bar>();
list.add(bar1);
list.add(bar2);
foo.bars = list;
m.marshal(foo, w);
} catch (Exception e) {
e.printStackTrace();
}
}
A workaround is to use #XmlElements annotation and tell the jaxb context all the possible implementations of IBar:
#XmlIDREF
#XmlElements( #XmlElement( type = Bar.class )/*, #XmlElement( type = AnotherBar.class) */ )
public IBar bar;

Categories

Resources