Does JAX-RS require data transfer objects (DTO)? - java

If a method of a JAX-RS application would return a domain object, the representation (say JSON) would contain all attributes of this object - right? But what if this object would contain "private" data, that shouldn't be exposed to the web?
And what is about the other direction from outside in: how could be prevented that private fields are overridden?
The only solution to this seems to create data transfer objects (dto).
To use an "automapper" wouldn't be the solution unless one can not specify what fields to map.
So, forces JAX-RS the developer to create DTOs? Or is there another solution?

For transparent marshalling and unmarshalling to and from XML of your entity, annotate it with JAXB annotations (a class can be annotated with both JPA and JAXB annotations and, this way, give an XML representation as well as be persisted in a database).
#Entity
#XmlRootElement
public class MyEntity implements Serializable {
#Id #GeneratedValue
private Long id;
....
}
In the above example I use only one JAXB annotation #XmlRootElement. Now, let's say that you don't want the id property in the serialized XML. Simply add the JAXB annotation #XmlTransient to it:
#Entity
#XmlRootElement
public class MyEntity implements Serializable {
#XmlTransient
#Id #GeneratedValue
private Long id;
....
}
So, no, there is no strict need for DTOs (and the boilerplate code to map them to and from entities).

I think it is better to say JAX-RS requires you to use representations.
My Foo domain object has no idea that it is being used in a RESTful manner. It only knows of Bar (another aggregate root) and whatever entities it can navigate from via that Bar. In fact, I also have a command-line interface to this application that doesn't use REST or even HTTP.
My RESTful interface wraps Foo/Bar in to representations that link to each other via URIs. I guess you can call these DTOs, but if you (like stated in other answers) just annotate your domain model with what is required to marshal and unmarshal them then I think you're coding yourself in to a corner that prohibits HATEOAS.
This is also apparent when you have a collection. If Foo->*Bar are you going to return all of the Bar items in their unmarshalled form? Why not just a URI and maybe some other minimal data,
e.g.
GET foo/fff
<foo>
<link rel="self" uri="uri="foo/fff" />
<bar uri="bar/abc123">
<status="Active" />
</bar>
<bar uri="bar/qqq">
<status="Inactive" />
</bar>
</foo>
If the client wants to know more about a given Bar, it can
GET bar/abc123
<bar>
<link rel="self" uri="bar/abc123" />
<foo uri="foo/fff" />
<status>Active</status>
<title>Some Bar</title>
...
</bar>

#XmlTransient (or a corresponding annotation) instructs the mappers/marshallers not to include the annotated property in the serialized output.

Related

JAXB ordering multiple properties

In my project, I would like to achieve specific order of properties in XML. In java object is represented as:
public class Plan {
private List<Act> act;
private List<Leg> leg;
...
An output object should look similar to this one:
<plan>
<act x="-25000" y="0"/>
<leg mode="car">
...
</leg>
<act x="10000" y="0"/>
<leg mode="car">
...
</leg>
</plan>
Is JAXB able to set up specific order for such case where I need to put items in order:
act.get(0)
leg.get(0)
act.get(1)
leg.get(1)
...
..
.
I know JAXB is able to save specific order of parameters like firstly act, then all legs, using #XmlType (propOrder={"prop1","prop2",..."propN"}) but it is not the case of this project as the 3rd party application which reads this xml's read them in pairs and propOrder would print them one by another.
Ok, I approach the problem from a different side and I solved it... Previously I thought it was a sorting problem - in fact, it is a problem with Java POJO class construction and JAXB annotations.
The solution for that is to use
#XmlElements({
#XmlElement(name="leg", type=Leg.class),
#XmlElement(name="act", type=Act.class)
})
#XmlElementWrapper(name="plan")
public List<Plan> getPlan() {
return plan;
}
and then items must be put one by another.
more details can be found in this answer:
Marshalling a List of objects implementing a common interface, with JaxB

How to extend non-modifiable model to use with JPA?

What's the best practice to create persistence (say via Spring Boot or just JPA or Hibernate itself) for a data model coming from a non-modifiable dependency? Typical limitations like not being able to override a field or what patterns like Decorator allow and what not slowed my progress down. I tried some things, but I always end up with the result that it would be necessary to either modify the source model (like adding annotations to make it natively compatible -> the fork I don't want) OR write a ton of wrapper code which would replicate the original model too much - but even this isn't working right now:
I tried
Creating a JpaRepository for the original class. Doesn't work, because casting the extended class to its parent class is not working.
Extend the original class with a custom class that gets necessary annotations like #Entity can be used in such a repository. But problems here were
that the original class is missing an #Id annotation, which could be fixed by using a new ID in the extended class, but
the given model also has a non-simple architecture, including lists of other classes that are part of the model itself. So other annotations like #ElementCollection might be necessary, which can't be added because overriding of fields is not possible.
Hiding it with creating a new field with the same name in the new class is not working:
An error like Could not determine type for: java.util.List, at table: yeah_this_one, for columns:[org.hibernate.mapping.Column(objects)] indicates that the original field can't be hidden completely (changed table and column name in new class to verify that).
So of course adding #ElementCollection (which is said to solve that) isn't helping here, too.
#AttributeOverride is also not working to override annotations to set the ID or other settings, only the name and column can be changed.
I'm stuck at this state and am wondering if this is even the right approach at all.
The setup or what I would expect to work from my understanding:
The general idea is based on this Spring Boot REST tutorial, which I tried to expand with a model from a dependency.
Let's assume there is the original model class Model from a dependency that can not be modified. The ModelEntity would be the extended class to act as way to pull the model into Spring persistence.
In the scope of the dependency the original class would be like:
// Given dependency, not modifiable
#Some existing annotation
public class Model extends AnotherClassFromDep {
#more annotations
private IdLikeClassFromDep modelId;
//more complex attribute
#Nullable
private List<RefClassFromDep> objects = new ArrayList<>();
// more attributes, getter, setter etc.
}
In the scope of my program:
In combination with this little additional orm.xml it is possible to annotate the original Model as MappedSuperclass without modifying it (according to https://stackoverflow.com/a/2516951/1844976).
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
<mapped-superclass class="package.name.of.original.Model">
</mapped-superclass>
</entity-mappings>
This allows to create a class like this, which extends the original POJO model to add JPA annotations:
#Entity
public class ModelEntity extends Model {
// some #Id attribute is necessary, which should correspond to
// the already existing ID attribute from the original `Model`
// in the best case, but an additional one would work too
private #Id #GeneratedValue Long id;
// Different approaches to solve the List error from above, for
// instance hiding the original attribute
#ElementCollection
private List<RefClassFromDep> objects;
public ModelEntity(){
super();
}
}
At the current state the issues are blocking me from going further. But, altogether I would expect this to work with a JpaRepository:
// of course, creating a JpaRepository with original `Model` wouldn't
// work, because it has no `#Entity`
public interface ModelRepository extends JpaRepository<ModelEntity, IdLikeClassFromDep> {
}
In a way that actually accessing it like that is possible:
#Configuration
public class LoadDatabase {
#Bean
CommandLineRunner initDatabase(ModelRepository modelRepository) {
return args -> {
// depending on the implementation above, either create a
// Model and cast it or directly create a ModelEntity, set
// attriubtes and save it through the JpaRepository
modelRepository.save(model);
};
}
}
Both more abstract and specific code-related ideas and comments would help me. Thanks!
In the old days, Jpa/Hibernate were configured via XML.
You needed to provide persistence.xml for general configuration. In this file, you added <mapping-file> tag pointing to another file orm.xml In this file you configured mapping for your entities (which is done via JPA annotations these days).
See https://vladmihalcea.com/how-to-use-external-xml-mappings-files-outside-of-jar-with-jpa-and-hibernate/
While the methods described above are considered legacy, they are still supported. LocalContainerEntityManagerFactoryBean has method setMappingResources allowing you to point to the orm.xml file. There is some funkiness about search paths and default locations, but it is well documented:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setMappingResources-java.lang.String...-
Note that the third-party class you are configuring this way needs to conform to Java Beans conventions (no-args constructor, getters and setters)

JAX-RS, with JAXB #XmlTransient

I am developing a RESTful service using JAX-RS and JAXB. I have a Complain class, following is a striped down version of it:
#Entity
#Table(name = "complain")
#XmlRootElement
public class Complain implements Serializable {
private String title;
private String description;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "complainidComplain")
private Collection<Comment> commentCollection;
#XmlTransient
public Collection<Comment> getCommentCollection() {
return commentCollection;
}
}
Note: I have decorated getCommentCollection with #XmlTransient annotation, because I don't want to see comment when I'm looking for all the complains at #Path("/").
example.com/api/complain/
<complains>
<complain>
<title>Foo</title>
<description>Foo is foo</description>
</complain>
<complain>
<title>Bar </title>
<description>Bar is bar</description>
</complain>
</complains>
But when I'm looking for a specific Complain at #Path("/{id}"), I need the comments to appear in the XML output.
example.com/api/complain/1
<complain>
<title>Foo</title>
<description>Foo is foo</description>
<comments>
<comment> Yes, i agree </comment>
<comment> Lorem if foo </comment>
</comments>
</complain>
Since I have decorated getCommentCollection with #XmlTransient annotation I can't get comments when I'm looking for a specific Complain at #Path("/{id}"). How can I achieve this?
Annotating with #XmlTransient is a compile-time decission so you can't change it dynamically on runtime. As discussed at How to conditionally serialize with JAXB or Jackson you could use Jacksons JsonView or MOXy's external mapping-files.
If you don't want to change your Serializer you could map Complain to a limited Class like e.g. ComplainPreview which has only the properties title and description. You could also simply set commentCollection to null before returning it in your JAX-RS resource method.
Last (and maybe cleanest) solution: Fetch only the data you want to return from the Database. Therefore you need different queries for your two use cases. You could for instance use a Constructor Expression for the first one:
select new com.yourcompany.Complain(c.title, c.description) from Complain c
Don't forget to add the correspondent Constructor.

JAXB - can class containment be flattened when marshalling to XML?

Say, I have two classes:
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlElement
B b;
}
class B {
#XmlElement
String propertyOfB;
}
JAXB returns an XML formatted in the according way:
<a>
<propertyOfA>valueA</propertyOfA>
<b>
<propertyOfB>valueB</propertyOfB>
</b>
</a>
My question is how to flatten the hierarchy in the XML? So that I have:
<a>
<propertyOfA>valueA</propertyOfA>
<propertyOfB>valueB</propertyOfB>
</a>
Can this be done with annotations?
At the moment I am thinking to create a kind of wrapper class for A, that would have fields built the way I want to see them in the XML. Is there a better way?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You could use MOXy's #XmlPath extension to map this use case:
import java.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlPath(".")
B b;
}
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
It's been a while for me, but let me give it a crack:
#XmlRootElement
class A {
#XmlElement
String propertyOfA;
#XmlElement(name="propertyOfB")
B b;
}
#XmlType(name="")
class B {
#XmlValue
String propertyOfB;
}
Edit: disclaimer- I havn't compiled or tried this. But I believe it's how you do it.
Take a look to that question and the solution proposed: Spring RESTful client: root tag exception
Very helpful to solve this kind of problem.
An alternative approach (with standard JAXB) would be using #XmlJavaTypeAdapter. This way you could adapt the object hierarchy with the drawback of having to write the code that translates the object hierarchy into the adapted new class.
In your example however it wouldn't work as you would have to adapt class A which is your root. If however the hierarchy was deeper and you needed to make the adaptation one level lower than the root, then there wouldn't be a problem. A suggestion that might be useful is to write the code in the adapted entity, making it like a delegate to the object hierarchy, rather than the adapter, which would then be very thin.

Handling nested elements in JAXB

I am wondering if it is possible to have JAXB not to create Java object for XML elements that serve as wrappers. For example, for XML of the following structure
<root>
<wrapper>
<entity/>
</wrapper>
</root>
I do not want an object for <wrapper> to be created at all. So for a class like
class Root {
private Entity entity;
}
the <entity> element should be unmarshalled directly into the entity field.
Is it possible to achieve with JAXB?
Although it requires extra coding, the desired unmarshalling is accomplished in the following way using a transient wrapper object:
#XmlRootElement(name = "root")
public class Root {
private Entity entity;
static class Entity {
}
static class EntityWrapper {
#XmlElement(name = "entity")
private Entity entity;
public Entity getEntity() {
return entity;
}
}
#XmlElement(name = "wrapper")
private void setEntity(EntityWrapper entityWrapper) {
entity = entityWrapper.getEntity();
}
}
EclipseLink MOXy offers a JAXB 2.2 implementation with extensions. One of the extended capabilities is to use XPath to navigate through layers of the XML you don't want in you domain model.
If you look at:
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/MOXyExtensions
you will notice that the Customer's name is stored within but that the name is a String attribute of Customer. This is accomplished using:
#XmlPath("personal-info/name/text()")
public String getName() {
return name;
}
I hope this helps,
Doug
Worth mentioning, if the content is a list of <entity/> instead of a single instance:
<root>
<wrapper>
<entity/>
<entity/>
...
</wrapper>
</root>
then you can use the #XmlElementWrapper annotation:
#XmlRootElement(name = "root")
public class Root {
#XmlElementWrapper(name = "wrapper")
#XmlElement(name = "entity")
private List<Entity> entity;
static class Entity { }
}
The whole point of JAXB or other mapping systems is to map the elements and their hierarchy to classes. In your example, you seem to want JAXB to somehow know that it can marshal entity into wrapper/entity and vice-versa without actually creating the class used for the wrapper mapping and the connection between root and entity. Which, as presented, is roughly equivalent to asking how to connect a car engine to the wheels without a driveshaft.
So, unless I am missing the point, the answer is no - neither JAXB or any other mapping program can do this. You can avoid creating classes by using something that does mapping purely dynamically (see Groovy, GPath for an example), but that avoids creating all classes, not just skipping one intermediate level in a hierarchy.

Categories

Resources