It's about passing interface of DTO to DAO.
For example I have following code
public interface User {
String getName();
}
public class SimpleUser implements User {
protected String name;
public SimpleUser(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
// Mapped by Hibernate
public class PersistentUser extends SimpleUser {
private Long id;
// Constructor
// Getters for id and name
// Setters for id and name
}
I'm using generic DAO. Is it ok if I create DAO with using interface User instead PersistentUser?
User user = new PersistentUser(name);
UserDao.create(user);
I read a lot of topics on stack but not figured out is this approach ok or no. Please help me. Maybe this is stupid and I can achive only problems.
About separating beans.
I did this because some classes I want to share via API module, that can be used outside to create entities and pass them to my application. Because they uses interface I developed so I can pass them to my DAO for persisting.
Generally, I would say it is ok, but there are a few hidden problems. A developer could cast the object down or access some state via a toString method that shouldn't be accessible. If you don't be careful, it could happen that state is serialized as JSON/XML in webservices that shouldn't be serialized. The list goes on.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(PersistentUser.class)
interface User {
String getName();
}
Querying could look like this
List<User> dtos = entityViewManager.applySetting(
EntityViewSetting.create(User.class),
criteriaBuilderFactory.create(em, PersistentUser.class)
).getResultList();
Related
I'm trying to customize the springdoc-openapi, make it can work with my framework, but I meet two problems.
1. How to treat methods that do not start with is/get as properties of Model?
If users use my ORM framework by Java language, the property getters in the entity interface can either start with is/get like a traditional Java Bean, or don't start with is/get like a Java record, for example
#Entity
public interface Book {
#Id
long id();
String name();
int edition();
BigDecimal price();
#ManyToOne
BookStore store();
#ManyToMany
List<Author> authors();
}
Here, the wording that does not start with is/get is used, which looks like a java record, not a traditional java bean.
However, doing this will cause swagger-ui to think that the model doesn't have any attributes. So I have to change the behavior of swagger.
After some research, I found that this behavior can be changed using io.swagger.v3.core.converter.ModelConverter, which is the most likely solution.
However, springdoc-openapi does not explain in detail how to use ModelConverter in the documentation. Ultimately, this goal was not achieved.
2. How to control the shape of dynamic objects in HTTP response?
My ORM is GraphQL-style, its entity objects are dynamic so that data structures of arbitrary shapes can be queried, just like GraphQL does. For example
#RestController
public class BookController {
#AutoWired
private JSqlClient sqlClient;
// Query simple book objects
#GetMapping("/books")
public List<Book> books() {
return sqlClient.getEntities().findAll(Book.class);
}
// Query complex book objects
#GetMapping("/books/details")
public List<Book> bookDetails() {
return sqlClient.getEntities().findAll(
// Like the request body of GraphQL
BookFetcher$
.allScalarFields()
.store(
BookStoreFetcher.$.allScalarFields()
)
.authors(
AuthorFetcher.$.allScalars()
)
);
}
}
The first query returns a list of simple book objects in the format {id, name, edition, price}
The second query returns a list of complex book objects in the format {id, name, edition, price, store: {id, name, website}, authors: {id, firstName, lastName, gender}}
Dynamic objects can vary in shape, and these are just two special cases.
I expect swgger to tell the client the shape of the object returned by each business scenario. So, I defined an annotation called #FetchBy. It should be used like this
#RestController
public class BookController {
private static final Fetcher<Book> BOOK_DETAIL_FETCHER =
BookFetcher$
.allScalarFields()
.store(
BookStoreFetcher.$.allScalarFields()
)
.authors(
AuthorFetcher.$.allScalars()
);
#AutoWired
private JSqlClient sqlClient;
#GetMapping("/books")
public List<Book> books() {
return sqlClient.getEntities().findAll(Book.class);
}
#GetMapping("/books/details")
public List<#FetchBy("BOOK_DETAIL_FETCHER") Book> bookDetails() {
return sqlClient.getEntities().findAll(BOOK_DETAIL_FETCHER);
}
}
Declare the shape of the complex object as a static constant.
The #FetchBy annotation uses the constant name to tell swgger the shape of the returned dynamic object.
After some research, I found that this behavior can be changed using org.springdoc.core.customizers.OperationCustomizer, which is the most likely solution.
However, I found that the schema tree of swagger is not consistent with the generic type definition tree in the java language. For example, Spring's ResponseEntity<> wrapper will be ignored by swagger and will be not parsed as a node of schema tree. Theoretically speaking, this ability of swagger can be customized infinitely, so the two trees may not always be consistent and difficult to analyze.
I have defined some beans to be stored in a Mongo Database. I am using Quarkus Panache MongoDB.
It works if I annotate the domain classes with #MongoEntity
#MongoEntity(collection="ThePerson")
public class Person {
public ObjectId id;
public String name;
...
}
I want to avoid the #MongoEntity in these classes, to put these clases in a separated package and not depend on the Panache, or even any quarkus dependency.
How can I configure these external beans?
Why? I want to share a package to help other teams that don't use quarkus but will use the same model.
What if you share an interface with all the getters for example? So you can easily share your interfaces in packages/libs.
With this approach, suppose you are going to expose your models over some REST apis returning Jsons for your clients, you can create a utility jar/package that automatically converts the Json for your pojos.
//your abstration
interface BasePerson {
ObjectId id();
String name();
...
}
//your model/entity
#MongoEntity(collection="ThePerson")
public class Person implements BasePerson {
public ObjectId id;
public String name;
...
}
I'm facing some problems to organize my mapping methods in my code. I'm doing the mapping logic in the controller layer but some entities needs diferents dtos to each operation (insert, update e delete).
I created a generic controller with 2 types: the original entity and the dto type. But this stucked it in this case with multiple dto representations. I'm not sure if create 3 generic types is a good way to handle this.
Another problem is that my controller layer is getting bigger with many mapping methods. Even using ModelMapper as automapper, some cases I prefer do it by myself than create complex converter classes.
How can I organize my dto mapping code and don't overload my controller with lots of mapping methods?
P.S: My project is a rest api with jax-rs, cdi and jpa
I suggest you to follow the following rules:
Separate the DTO that serve the API from the other domain classes, and use naming convention to quickly identify it (ex. XxxAPI). You may organize in a resource dedicated package : the controller, DTO and mapper classes.
Don't be afraid to write a lot of code, especially in mapper classes, you may use some IDE tricks to generate and test them.
Be careful using automapper, too much magic is dangerous, you may consider Builder patterns to facilitate your DTO / Domain mapping
Regards.
A common approach is to split the DTO conversion logic into its own class. Depending on the size of the project, creating a repository class may also be useful. This leaves us with three classes:
Controller: Performs the REST operations
Repository: Obtains the DTOs from a database or Data Access Object (DAO)
DTO Mapper: Converts the DTO to a domain object
The repository allows us to hide the DTO from the controller entirely. Instead, the controller will only deal with domain objects and is made unaware that a conversion from a DTO to a domain object occurred at all.
The repository (let's say for Foo domain objects created from FooDto objects) is:
public Foo {
private long id;
private String name;
// ...getters & setters...
}
public interface FooRepository {
public List<Foo> findAll();
public Optional<Foo> findById(long id);
public Foo create(long id, String name);
public Foo update(long id, String name);
public void delete(long id);
}
The DTO conversion logic would be:
public class FooDto {
private long id;
private String name;
// ...getters & setters...
}
public class FooDtoMapper {
public Foo fromDto(FooDto dto) {
Foo foo = new Foo();
foo.setId(dto.getId();
foo.setName(dto.getName();
return foo;
}
public FooDto toDto(Foo foo) {
FooDto dto = new FooDto();
dto.setId(foo.getId();
dto.setName(foo.getName();
return dto;
}
}
With the FooDtoMapper created, we can create a FooRepository implementation:
public class DatabaseFooRepository implements FooRepository {
#Inject
private DatabaseConnection dbConnection;
#Inject
private FooDtoMapper mapper;
#Override
public List<Foo> findAll() {
return dbConnection.getAllFromCollection("FOO", FooDto.class)
.stream()
.map(mapper::fromDto)
.collect(Collectors.toList());
}
// ...implement other methods
}
The dbConnection object is an abstraction of the database from which the DTOs are being pulled from. In this example, we can assume that getAllFromCollection("FOO", FooDto.class) returns a List<FooDto>, which we then stream and convert to a List<Foo> using the FooDtoMapper object (mapper). In your project, this will likely be replaced with JPA-specific code, but the principle still remains the same: Obtain DTOs from the JPA interfaces and convert them into domain objects using the mapper object.
This results in the following controller logic:
#Path("foo")
#Controller
public class FooController {
#Inject
private FooRepository repository;
#GET
public Response findAll() {
List<Foo> foos = repository.findAll();
Response.ok(foos);
}
// ...other controller methods...
}
Using this pattern, we abstract the logic for converting from DTOs into its own class and the controller is only responsible for dealing with domain objects.
In general, it is preferable to have many, simple classes that do one thing rather than putting all of the logic in a single class (like your original controller) in hopes of reducing the number of classes. For example, the FooDtoMapper is responsible for only converting from FooDto objects to Foo objects, and vice-versa. The DatabaseFooRepository is responsible only for getting DTOs from the database and using the FooDtoMapper to convert the DTOs to domain objects (i.e., getting domain objects from the database). And lastly, the FooController is only responsible for getting domain objects from the FooRepository (i.e., DatabaseFooRepository at run-time) and providing the necessary REST API metadata (i.e., HTTP status OK).
Note that in this case, the Foo and FooDto objects are identical and there isn't much of a reason to have separate Foo and FooDto objects (i.e., why not just store Foo objects in the database instead of FooDto objects?), but that is not always the case. Generally, the domain object and the DTO will vary. For example, the domain object may have a currency amount or date that must be converted to a String or other data structure that can be stored in the database (the DTO would have this String field while the domain object would have the actual field, such as a currency or date). In this case, I made the domain object and DTO the same for the sake of simplicity.
In my application I have multiple objects that I would like to add comments to. Every one of these objects is represented in its own database table. Beside being connected to a specific object, all comments share a common context in which the corresponding objects exist. What I tried for now is to use JPA-inheritance with InheritanceType.SingleTable so I can store the foreign keys to every 'commentable'-object in one table and the discriminator-feature to seperate that table into different Comment-Subclasses in JPA:
Superclass Comment.java
#Entity
#Table(name = "COMMENT_TABLE")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "COMMENT_OBJECT_TYPE")
public class Comment {
protected String text;
protected CommonContext context;
...
}
Subclass Object A
#Entity
#DiscriminatorValue(value = "OBJECT A")
public class ObjectAComment extends Comment {
private ObjectA objectA;
// OneToMany-Relation exists in Object A accordingly
#JoinColumn(name = "FK_OBJECT_A")
#ManyToOne
public ObjectA getObjectA() { return objectA; }
public void setObjectA(ObjectA objectA) { this.objectA = objectA; }
}
The other comment-classes for the other objects are designed just as for object A. The common context shall be used to get all comments for a specific situation and I would like to have a comment know its owner, so that I can easily link to that object in my application. Without the latter I had to go through all objects of that type to search for any that has comments, as not every object has them.
On designing the REST-endpoints and the EJBs I ended up creating specific methods for every subclass of Comment.java. For example for creating a comment I have
#POST
#Path("comments/objectA")
public Response createCommentForObjectA(ObjectAComment comment) { ... }
#POST
#Path("comments/objectB")
public Response createCommentForObjectB(ObjectBComment comment) { ... }
...
This feels a bit cumbersome as I would rather have
#POST
#Path("comments")
public Response createComment(Comment comment) { ... }
which is impossible with the current design as I would lose the specific information for the different objects. Now I see three possible ways to go on:
Version 1
I stick with the current solution and create CRUD-methods for every type of comment.
Version 2
A friend suggested, that I could use transient properties in Comment.java:
public class Comment {
...
private COMMENT_OBJECT_TYPE objectType;
private long idObject;
#Transient
public long getIdObject() { return idObject; }
...
#Transient
public COMMENT_OBJECT_TYPE getObjectType() { return objectType; }
...
}
With this I could generalize the parameter of the REST-endpoint and return specific objects depending on the object type:
#POST
#Path("comments")
public Response createComment(Comment comment) {
// return ObjectAComment, ObjectBComment, ... depending on the object type
}
Version 3
Ditch the whole #Inheritance and #DiscriminatorColumn, put everything in one JPA-class and do the whole organizing of the comment context myself. Additionaly I would lose type safety.
None of these solutions feels completely right to me, hence I would like to ask if there is a preferable way to design this kind of comment feature and is there maybe something I am missing completely?
Edit 1
Added information that all comments and objects share a common context. Renamed previous COMMENT_CONTEXT to COMMENT_OBJECT_TYPE to avoid a misunderstanding between this common context and the object type a comment is related to.
It seems to me like your Comment is a standalone entity without dependencies on other entities. A comment has an owner, but should not know who is owning it. So I would add a column on the comment table "ownerUuid". The entire comments "bounded context" knows nothing about the other entities.
When creating a comment you always provide the ownerUuid. Same for retrieval. So you can create comments for any entity having a uuid.
However this means you need to add UUID columns in your already existing tables.
This to me seems like the cleanest solution. This way your comment system can stand on it's own without heavy impact on other entities.
EDIT
Because of the extra information. I would suggest following approach.
Keep in mind that I do not know how the owner of the Comment is used, so the suggestion might not be perfect for this scenario.
As the comment should know the owner object I would suggest doing the following:
Have an interface CommentOwner with methods:
getUuid()
getContext()
Any other information you might need from the owner
A Comment will have a CommentOwner property.
Every entity that you want to contain Comments, should implement this interface. When creating a Comment you provide the CommentOwner.
This way you can retrieve comments Based on Context. A Comments has a direct link to its owner, but still does not need to know about the specific classes of the owners.
In the end I went with version 3, keeping every information in one Comment-class. The subclasses which I would've achieved with #Inheritance and #DiscriminatorColumn would only have one property, the foreign key to the commentable object, and wouldn't differ in what they represent in general and how they would be used.
My class looks something like this now:
public class Comment {
private String text;
private CommonContext context;
private COMMENT_OBJECT_TYPE objectType;
private ObjectA objectA;
private ObjectB objectB;
...
#JoinColumn(name = FK_OBJECT_A)
#ManyToOne
public ObjectA getObjectA() { return objectA; }
public void setObjectA(ObjectA objectA) { this.objectA = objectA; }
...
}
I have a entity class "classA" and a model class "ClassA1". ClassA is used to store the values in mysql database from server side and ClassA1 is used to convert a API response string into object which is client side.
Now I am having two java classes with same getters and setters but ClassA contains hibernate annotations and ClassA1 is simply a POJO. Consider the following class structures
ClassA.java
#Entity
#Table(name="classA")
public class ClassA {
#Id
private int id;
private String name;
public int getId() {
return id
}
public void setId(int id) {
this.id = id;
}
#Column(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ClassA1.java
public class ClassA1 {
private int id;
private String name;
public int getId() {
return id
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My application is containg more number of classes like the above. I want to avoid this, because If I add any column to my database, currently I am adding getters and setters in both ClassA and also in ClassA1.
Is there any way to use a single class for both server and client side modules?
You can simply try to use same class in both situation. It should work. That's the way I tried in the beginning of one of my earlier project. As long as you can carefully deal with JPA merging of the ClassA you received from client-side by treating that as a detached entity.
However, when your entity design becoming more complicated, you will face a lot of problem (and that's why I switch away from this approach in my project :P). One of the biggest problem is, given you have modelled the entity relationships, once you try to "serialize" it for client use, a lot of (JAXB etc, at that time I was doing the projects) solution is going to recursively trace through all the relationship and try to convert it. Such behavior will trigger massive lazy fetching of entities, and make the result serialized form contains huge amount of (unused) data. Although you can control the behavior (by adding some kind of ignore annotation etc), the result entity will become messy to maintain.
Therefore what you are doing, imho, is not unreasonable. What you need to beware is, treat the value object (what you called "Model") something for "presentation". You don't need to make it strictly the same as your entities.
Make use of / develop some util library to handle the construction of value object from entities, and populating data from value object back to entities.
Of course, the suggestion may not apply to you, given that you haven't share much on your architecture.
You could specify your Hibernate mappings separately in an XML file instead of using annotations. This is the old way of doing it, nowadays most people use the annotations because it is more convenient and follows the JPA standard (mostly).
Another solution is to use a bean mapping framework like Dozer to make the mapping easier.
It's quite common to separate your persistent (JPA) Entities and value objects used by views in your architecture. You can customize the value objects for the view in which they are used. Maybe the view does not need the full User entity, but only the id, name and address? If this is the case it makes the communication between view and backend lighter and partially resolves the duplication between your ValueObjects and persistent entities.