I have the following case:
**public class SimpleSource {
private String name;
private String description;
// getters and setters
}
public class SimpleDestination {
private String name__c;
private String description__c;
// getters and setters
ContactMapper contactMapper = Mappers.getMapper(ContactMapper.class);
#Mapping(source = "name", target = "name__c")
#Mapping(source = "description", target = "description__c")
Target__c customerContact(Source source);**
How can I avoid adding this #Mapping for all fields and just said that target has suffix "__c" targetFieldName = sourceFieldName+suffix
MapStruct uses its AccessorNamingStrategy SPI to detect names of properties.
This means that in order to achieve what you are looking for you'll need to provide you own AccessorNamingStrategy.
You can read more about it in the MapStruct Documentation
Related
Consider the following POJOs:
public class PersonVo {
private String firstName;
private String lastName;
}
private class PersonEntity {
private String fullName;
}
Using MapStruct, I want create mapper that PersonVo to PersonEntity.
I need mapping multiple source fields firstName, lastName to one target filed fullName.
Here is pseudo code what I want to.
[Want Solution A]
#Mapper
public interface PersonMapper {
#Mapping(target = "fullName", source = {"firstName", "lastName"}, qualifiedByName="toFullName")
PersonEntity toEntity(PersonVo person);
#Named("toFullName")
String translateToFullName(String firstName, String lastName) {
return firstName + lastName;
}
}
[Want Solution B]
#Mapper
public interface PersonMapper {
#Mapping(target = "fullName", source = PersonVo.class, qualifiedByName="toFullName")
PersonEntity toEntity(PersonVo person);
#Named("toFullName")
String translateToFullName(PersonVo pserson) {
return pserson.getFirstName() + pserson.getLastName();
}
}
Is there any way this can be achieved?
Here is my answer.
#Mapper
public interface PersonMapper {
#Mapping(target = "fullName", source = ".", qualifiedByName="toFullName")
PersonEntity toEntity(PersonVo person);
#Named("toFullName")
String translateToFullName(PersonVo pserson) {
return pserson.getFirstName() + pserson.getLastName();
}
}
Main point is that
#Mapping(target = "fullName", source = ".", qualifiedByName="toFullName")
It can make use source object by parameter.
First I would go with public abstract class for Mappers. Makes it easier to extend them and create inheritance between the generated code and the abstract class. But here is your solution:
You can achieve this by creating a #AfterMapping annotated method. So something like
#AfterMapping
default void concat(#MappingTarget PersonEntity person, PersonVo person) {
... manipulate the target value
}
MapStruct documentation
I'm trying to implement inheritance with Kotlin and JPA. My abstract base class (annotated with #Entity) holds the ID (annotated with #Id and #GeneratedValue) and other metadata, like createDate, etc. I'm getting several errors from Hibernate, one for each field except the ID:
org.hibernate.tuple.entity.PojoEntityTuplizer - HHH000112: Getters of lazy classes cannot be final: com.example.BaseEntity.createDate
As I've read I need to include the open keyword for each property.
I have 3 questions regarding this:
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Why isn't it complaining about the ID?
It seems to work without the open keyword, then why is the error logged?
Edit:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class BaseEntity(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0,
val createdAt: Instant = Instant.now()
)
#Entity
class SubClass(
val someProperty: String = ""
) : BaseEntity()
I'm using the JPA plugin for Gradle, which I believe creates the noarg constructor, that's why I don't have to specify everything nullable.
Thank you!
The logged error has to do with lazy loading.
Hibernate extends entities at runtime to enable it. It is done by intercepting an access to properties when an entity is loaded lazily.
Kotlin has flipped the rules and all classes are final by default there. It is the reason why we're advised to add an open keyword.
If a property is not open hibernate cannot intercept access to it because final methods cannot be overridden. Hence the error.
Why isn't it complaining about the ID?
Because #Id is always loaded. There is no need to intercept access to it.
It seems to work without the open keyword, then why is the error logged?
The key word here is seems. It may introduce subtle bugs.
Consider the following #Entity:
#Entity
public class Book {
#Id
private Long id;
private String title;
public final Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public final String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
And the #Test:
#Test
public void test() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal here
Book book = new Book();
book.setId(1L);
book.setTitle("myTitle");
entityManager.persist(book);
// noise
entityManager.getTransaction().commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal
Book reference = entityManager.getReference(Book.class, 1L);
String title = reference.getTitle();
assertNull(title); // passes
entityManager.getTransaction().commit();
entityManager.close();
}
This test passes but it should not (and fails if getTitle is not final).
This would be hard to notice
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Looks like Hibernate gives up when it sees final #Entity.
Add open to SubClass and you will the precious:
2019-05-02 23:27:27.500 ERROR 5609 --- [ main] o.h.tuple.entity.PojoEntityTuplizer : HHH000112: Getters of lazy classes cannot be final: com.caco3.hibernateanswer.SubClass.someProperty
See also:
final methods on entity silently breaks lazy proxy loading
How to avoid initializing HibernateProxy when invoking toString() on it? - my old question (note that Hibernate uses Byte Buddy these days).
PS
Did you forget to include #MappedSuperclass on BaseEntity?
Without the annotation it should fail with something like:
org.hibernate.AnnotationException: No identifier specified for entity: com.caco3.hibernateanswer.SubClass
I have several POJOs which will have a monetary amount. My idea is to create a generic object MonetaryAmount (consisting of a currency and a value), which will then be used whenever I want to represent a monetary amount in one of my POJOs:
public class MonetaryAmount {
private String currency;
private BigDecimal value;
}
public class Account {
#Column(name = "ACCOUNT_NAME")
private String name;
private MonetaryAmount balance; // TODO set column annotation values of currency and value
}
Since MonetaryAmount will be used in several POJOs, I couldn't annotate the currency and value attributes with the #Column since the column name will not always be the same in all cases. Is there any way to annotate MonetaryAmount attributes (e.g. balance in the example above) to provide the column name for the currency and value attributes in a way that jOOQ understands them when mapping/unmapping a POJO similar to how Hibernate interprets the #AttributeOverride annotation please?
The #Embeddable annotation is currently (jOOQ 3.11) not supported by jOOQ's DefaultRecordMapper yet. The relevant feature requests are:
https://github.com/jOOQ/jOOQ/issues/2360
https://github.com/jOOQ/jOOQ/issues/2530
https://github.com/jOOQ/jOOQ/issues/6518
What you can do already now, if you're not using the JPA annotations on your POJOs, is to use the following aliasing notation in your query:
ctx.select(
ACCOUNT.ACCOUNT_NAME.as("name"),
ACCOUNT.CURRENCY.as("balance.currency"),
ACCOUNT.VALUE.as("balance.value"))
.from(ACCOUNT)
.fetchInto(Account.class);
This feature is documented in DefaultRecordMapper, see:
If Field.getName() is MY_field.MY_nested_field (case-sensitive!), then this field's value will be considered a nested value MY_nested_field, which is set on a nested POJO that is passed to all of these (regardless of visibility):
Single-argument instance method MY_field(...)
Single-argument instance method myField(...)
Single-argument instance method setMY_field(...)
Single-argument instance method setMyField(...)
Non-final instance member field MY_field
Non-final instance member field myField
Assuming Hibernate : You can used Embedded components.
#Entity
public class Account implements Serializable{
#Column(name = "ACCOUNT_NAME")
private String name;
#Embedded
#AttributeOverrides( {
#AttributeOverride(name="currency", column = #Column(name="CURRENCY") ),
#AttributeOverride(name="value", column = #Column(name="VALUE") )
} ) private MonetaryAmount balance;
}
#Embeddable
public class MonetaryAmount implements Serializable{
private String currency;
private BigDecimal value;
}
Though this should work, I think in your case you should try inheritance and still use same approach to override attributes in Object Oriented way.
I have a entity, name Product.It have two property is unit (byte) and unitName(String). unit property is mapped on database. Ex: 0:Kg ; 1:g;.... I want when input a valid unit, unit property is stored; unless, it save to unitName
Product
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private int productId;
#Column(name = "product_name")
private String productName;
#Column(name = "unit")
private Byte unit;
#Transient
private String unitName;
}
In unit text field, I use a UnitConvert
UnitConvert
public class UnitConverter implements IConverter<Byte> {
private static final long serialVersionUID = 4798262219257031818L;
public UnitConverter() {
}
#Override
public Byte convertToObject(String value, Locale locale) {
return Text.isEmpty(value) ? 0 : UtilCommon.getTaniCode(value);
}
#Override
public String convertToString(Byte value, Locale locale) {
return (value == null || value==0 ) ? "" : UtilCommon.getTaniName(value);
}
}
I only think about HiddenField to do that, but I don't know how to do that.
Someone know how to use or anything can help me. Thank you very much
So from what I understood you want to save the input of a Model to a different database property depending on certain checks before hand. You can do that in your Form.onSubmit() method.
A very simple implementation could look like this:
public ProductPanel(String id, final IModel<Object> productModel) {
super(id, productModel);
// we're putting the productModel into the constructor.
// Therefore it's guaranteed to be detached
// -> it's okay to have it with final modifier.
IModel<String> formModel = Model.of("");
Form<String> form = new Form<String>("form", formModel) {
#Override
protected void onSubmit() {
super.onSubmit();
String productName = getModelObject();
Object realProduct = productModel.getObject();
if (isAcceptableUnit(productName)) {
realProduct.setUnit(parseUnit(productName));
} else {
realProduct.setUnitName(productName);
}
layer.saveProduct();
}
};
add(form);
TextField<String> productName = new TextField<String>("textField", formModel);
form.add(productName);
}
private boolean isAcceptableUnit(String productName) {
// your logic to determine if it's okay to cast to byte here...
return true;
}
private byte parseUnit(String productName) {
// your logic to parse the String to byte here...
return 0;
}
Some additional comments since I'm uncertain if the code snippets you provided are just for simplicity or actually code pieces:
You should try to avoid declaring your db object Serializable. Should you use normal Model objects to save your DTOs wicket will actually serialize them and you won't be able to do anything with them (well with hibernate at least).
Database object should use LoadableDetachableModel and save the primary key to load the entity in the load() method of it.
This would enable you now to work directly on those objects by using CompoundPropertyModel etc (which has it's pros and cons which I will not explain in detail here).
Still in your case I would add an Model<String> to the form and let the server decide how the input should be handled and mapped to the actual domain object.
I'm using OpenJPA with QueryDSL, I try to avoid manipulating Tuple objects by using the bean projections features of QueryDSL. I have for example these two Entity, with a #ManyToOne relation.
#Entity
public class Folder {
private Long id;
private String name;
private String path;
#ManyToOne
#JoinColumn(name = "FK_FILE_ID")
private File file;
}
#Entity
public class File {
private Long id;
private String fileName;
}
When I'm executing this query :
List<Folder> listFolders = query.from(folder)
.list(Projections.bean(Folder.class, folder.name, folder.file.fileName));
I have an error saying that the Folder object doesn't contain the fileName property.
I understand what QueryDSL is doing, since it is a simple "flat" projection, but I would like to know if it is possible to fill the fileName attribute of my folder.file object with the found value by the query.
NB : I know that I can define a constructor for my Folder class and use this method :
query.list(ConstructorExpression.create(Folder.class, folder.name,
folder.file.fileName));
But I want to avoid this if possible, because it forces me to define N-constructors, for the N-combinations of fields I want in my projections.
You can use nested projections for this case
List<Folder> listFolders = query.from(folder)
.list(Projections.bean(Folder.class, folder.name,
Projections.bean(File.class, folder.file.fileName).as("file")));
Here is a more explicit alternative to constructor and bean projection that should also work for this case
MappingProjection<Folder> mapping = new MappingProjection<Folder>(Folder.class, folder.name, folder.file.fileName) {
#Override
protected Folder map(Tuple row) {
Folder f = new Folder();
f.setName(row.get(folder.name));
File file = new File();
file.setFileName(row.get(folder.file.fileName));
f.setFile(file);
return f;
}
};
Related
http://www.querydsl.com/static/querydsl/3.6.0/apidocs/com/mysema/query/types/MappingProjection.html