Eradicate boilerplate - java

public class Toponym {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition="TEXT default ''", nullable = false)
public String name;
}
public class LevelOneEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition = "boolean default false", nullable = false)
private boolean archived;
}
public class LevelTwoEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(columnDefinition = "boolean default false", nullable = false)
private boolean archived;
}
These two classes definitely have some boilerplate code. If multiple inheritance were a reality, I'd organize two mixins here: IdMixin and ArchivedMixin. Therefore classes would contain no bodies at all. But in Java it is not possible.
It may be possible to use multiple interfaces but they can't contain the code itself if I'm not mistaken.
How to cope with such a problem in Java?

If those annotations are allowed on methods, not only on fields (e.g. getters or setters), you can declare them in interfaces and implement as many interfaces as you want. The fields will still be declared in each derived class, though, but you won't need to restate the annotations for them.
Otherwise, you'll have to create a class hierarchy that allows for the flexibility you need:
class Entity // contains annotated ID field and declares the generic ID type
class ArchivedEntity extends Entity // if you don't expect to have non-entity archived classes (i.e. archived objects with no ID)
... etc
This can get pretty complex as you add more combinations.

Related

possible alternatives to #GeneratedValue with #EmbeddedId in JPA

My entity has a wrapped Identifier like this,
#Entity
public class Article {
#EmbeddedId
private ArticleId articleId;
....
}
#Embeddable
public class ArticleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
in my architecture, multiple application instances(it all same.) are connected to the same data source
so #GeneratedValue(strategy = GenerationType.IDENTITY)seems good
because even if instances A and B try to create Accountat the same time, Its Id is guaranteed by the database.
problem is that #GeneratedValue is only able to use with #Id annotation (#Id is not available for EmbeddedId)
PersistenceUnitUtil.getIdentifier(Object entity) could be a alternative? like this,
ArticleId articleId = ArticleRepository.nextIdentity();
I am not sure that whether it causes the race condition.
Could PersistenceUnitUtil.getIdentifier(Object entity) guarantee the unique id across the different application instance(JVM)? I don't think so.
In this situation, What alternative is possible?
One solution could be to use an #IdClass to get rid of the nested property and be able to generate the identifier (since nested properties are "assigned" and cannot be generated, so the call of PersistenceUnitUtil.getIdentifier(Object entity) would not help here). See e.g. here for a complete guide (also linked in the linked answer by #SternK from the comment)
An #IdClass could look like this:
public class ArticleId implements Serializable {
private Long id;
}
An entity could use it:
#Entity
#IdClass(ArticleId.class) // specified dependency
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
// expose "typed" id:
public ArticleId getId() {
return new ArticleId(id);
}
}
Spring-Data #Repositorys also work with corresponding #IdClass objects, e.g.:
#Repository
public interface UserEntityRepository extends JpaRepository<Article, ArticleId> {
}
// would offer e.g. this method:
repository.findById(new ArticleId(123L));

Compare two class attributes using a JPA method

I want to compare two attributes using JPA method convention.
This is my class
#Entity
#Table(name = "aircrafts")
public class Aircrafts {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Size(max = 45)
#Column(name = "number", length = 45)
private String number;
#Column(name = "capacity")
private int capacity;
#Column(name = "seats_taken")
private int seatsTaken;
}
And this the method I want to implement :
public interface AircraftsRepository extends JpaRepository<Aircrafts, Long> {
public List<Aircrafts> findBySeatsTakenLessThanCapacity();
}
However I got this exception:
PropertyReferenceException: No property lessThanCapacity found for type int! Traversed path: Aircrafts.seatsTaken.
I've tried using int and Integer but I got the same exception. Which is the correct method name?
I think #benji2505, correctly mentioned that the data model mixes things that "should" be in different tables. Normally one would expect two tables: Aircrafts, Flights.
Then you could easily use:
List<Flight> flightsWithFreeSeats = aircraftRepository
.findAll()
.stream()
.map(aircraft ->
flightRepository.findByAircraftAndSeatsTakenLessThen(aircraft, aircraft.getCapacity)
)
.collect(Collectors.toList)
With current model probably #JZ Nizet already posted the best answer in his comment.

Using a Single DTO object for projection

I'm having trouble about having to use a single DTO object or a DTO object for every Entity object.
For example I have 3 classes: Book, Author and Publisher.
Book.java
#Entity
#Table(name = "tbl_book")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "description")
private String description;
other different fields...
}
Author.java
#Entity
#Table(name = "tbl_author")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "description")
private String description;
other different fields...
}
Publisher.java
#Entity
#Table(name = "tbl_publisher")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "description")
private String description;
other different fields...
}
and MyDTO.java
public class MyDTO {
private Long id;
private String description;
constructor id, description...
getters and setters...
}
I want to select only specific fields (id and description) with EntityManager:
em.createQuery("SELECT NEW MyDTO(id,description) FROM Book");
But my question is should I use a single DTO object(which is MyDTO) for all projection? Something like:
em.createQuery("SELECT NEW MyDTO(id,description) FROM Author");
em.createQuery("SELECT NEW MyDTO(id,description) FROM Publisher");
Tutorials about using DTO for projection just saying use DTO for read and use Entity for write but they are not telling about having a single object DTO or not. Will you please provide an example why single DTO or why DTO for every Entity. Thank you!
I've been working with DTOs for a while now, and here's some things I can share:
Try building a DTO hierarchy based on your needs (abstract classes and/or interfaces)
Even if your BookDTO 'just' extends a superclass (AbstractIdDescriptionDTO for instance), it's always more readable (IMO), and it's less tedious to maintain, and you can't misinterpret an object for another
I never use the new DTO syntax, I implemented an automatic converter instead
In my opinion create a DTO for each field. Having a single DTO couples your objects a lot. For example if in Book class you change the description to something else all your code will not work anymore.
Even if you start with thinking of a good name for the DTO you will see the problem. "MyDTO" doesn't sounds good and doesn't show its purpose but you won't be able to think of a meaningful name.
And last but not least there is the single responsibility principle that is a good thing and having one DTO to present multiple objects kind of breaks it. It's not a good practice and breaks the SOLID principle to have one class that represents different types of objects. If your projects share some common properties it's better to have separate classes and create an Interface for the common part. That's just standard OOP design. It's not related to DTOs
That's just my opinion. I might be wrong.
Also creating the DTOs like that in the JPQL query kind of makes it hard coded and harder to maintain. You can do something like:
.setResultTransformer( Transformers.aliasToBean( PostDTO.class ) )
for your query or there are other ways to do it.

How to create meta annotations on field level?

I have this hibernate class with annotations:
#Entity
public class SimponsFamily{
#Id
#TableGenerator(name = ENTITY_ID_GENERATOR,
table = ENTITY_ID_GENERATOR_TABLE,
pkColumnName = ENTITY_ID_GENERATOR_TABLE_PK_COLUMN_NAME,
valueColumnName = ENTITY_ID_GENERATOR_TABLE_VALUE_COLUMN_NAME)
#GeneratedValue(strategy = GenerationType.TABLE, generator = ENTITY_ID_GENERATOR)
private long id;
...
}
Since I don´t won´t to annotate every id field of my classes that way, I tried to create a custom anotation:
#TableGenerator(name = ENTITY_ID_GENERATOR,
table = ENTITY_ID_GENERATOR_TABLE,
pkColumnName = ENTITY_ID_GENERATOR_TABLE_PK_COLUMN_NAME,
valueColumnName = ENTITY_ID_GENERATOR_TABLE_VALUE_COLUMN_NAME)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface EntityId {
#GeneratedValue(strategy = GenerationType.TABLE, generator = ENTITY_ID_GENERATOR)
public int generator() default 0;
#Id
public long id() default 0;
}
so that I can use this annotation in my class:
#Entity
public class SimponsFamily{
#EntityId
private long id;
...
}
I do have to write the #Id and the #GeneratedValue annotions on field level since they do not support the TYPE RetentionPolicy. This solutions seems to work.
My questions:
How are the field level annotations in my custom annotations(and values) transferred to my usage of EntityId annotation?
What about the default values which I set in my custom annotation, are they used since I do not specify attributes at the usage?
It is a preferred way to use annotations on field level in annotations?
I think I can aswer your third question.
One common way to do what you want (avoid duplicating ID mapping) is to create a common superclass that holds the annotated id and version (for optimistic locking) fields, and then have all persistent objects extend this superclass.
To ensure the superclass is not considered an Entity on its own, it must be annotated with #MappedSuperclass.
Here is a sample (sorry for typos, I don't have an IDE at hand right now) :
#MappedSuperclass
public class PersistentObject {
#Id // Put all your ID mapping here
private Long id;
#Version
private Long version;
}
#Entity
public class SimpsonsFamily extends PersistentObject {
// Other SimpsonFamily-specific fields here, with their mappings
}

Applying annotations to fields inherited from #MappedSuperclass

Has:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
protected long id;
#Column(name="field")
private long field;
}
and
#Entity
class Subclass extends Superclass {
}
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
AFAIK, you can't. What you can do is overriding attributes and associations (i.e. change the column or join column) using the AttributeOverride and AssociationOverride annotations. But you can't do exactly what you're asking.
For the GeneratedValue, consider using XML mapping to override the strategy if you don't want to declare it in the mapped superclass.
For the Index (which is not a standard annotation by the way), did you actually try to declare it at the table level using Hibernate's Table annotation instead (I'm assuming you're using Hibernate)?
#Table(appliesTo="tableName", indexes = { #Index(name="index1", columnNames=
{"column1", "column2"} ) } )
creates the defined indexes on the
columns of table tableName.
References
JPA 1.0 Specification
Section 2.1.9.2 "Mapped Superclasses"
Section 9.1.10 "AttributeOverride Annotation"
Section 9.1.11 "AttributeOverrides Annotation"
Section 9.1.12 "AssociationOverride Annotation"
Section 9.1.13 "AssociationOverrides Annotation"
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions
Chapter 3. Overriding metadata through XML
As for #GeneratedValue, it is possible to do like this:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
#GeneratedValue(generator = "id_generator")
protected long id;
#Column(name = "field")
private long field;
}
#Entity
#SequenceGenerator(name = "id_generator", sequenceName = "id_seq")
class Subclass extends Superclass {
}
You might be able to do this if you apply the annotations to the accessor methods instead. (I haven't tried this, so I can't guarantee that it'll work.)
#MappedSuperclass
public class Superclass {
#Id
#Column(name = "id")
public long getId() {
return id;
}
.
#Entity
public class Subclass extends Superclass {
#GeneratedValue
public long getId() {
return super.getId();
}
Just in case anyone else searches for this, I used the following code which adds in some overhead, but for processing Field annotations only shouldn't add that much:
private List<Field> getAllFields() {
List<Field> fieldList = new ArrayList<Field>();
// Add all fields from the current class
fieldList.addAll(Arrays.asList(mElement.getClass().getDeclaredFields()));
// Use an index to iterate over mElement's parent types
Class clazz = mElement.getClass();
// Get any fields from the parent class(es)
while (clazz.getSuperclass() != null) {
fieldList.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
// Set it to that parent class
clazz = clazz.getSuperclass();
}
return fieldList;
}
The returned list would contain all fields for all parent and child classes with mElement being the object you are searching for annotations from. Hope this helps.

Categories