I am trying to save a byte[] field to local filesystem instead of the database.
I have tried the JPA annotation #Transient
#Entity
#Table(name = "screenshot")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Screenshot implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Lob
#Transient
#Column(name = "image", nullable = false)
private byte[] image;
#Column(name = "otherField", nullable = false)
private String otherField;
#Column(name = "otherField2", nullable = false)
private String otherField2;
}
But after I persist the entity, for e.g. The returnedEntity image property will not be returned due to #Transient annotation.
Screenshot returnedEntity = screenshotRepository.save(entity);
But I need to persist it in the database first in-order to get an unique ID and use this ID as part of the file path to persist only the image(binary) field in my local filesystem.
I run into a situation that before I save the entity into the database, I do not have an unique ID. But after I save the entity, I lost the binary byte[] data to save the file in the local filesystem.
Can't seems to figure a good way to link the saved ID to the binary byte[].
Your problem is that you want the field to be Transiented and Included (serialized) at the sametime. Some people suggest to use #JsonInclude annotation, like what is mentioned here.
However, from my point of view, the idea is in the logic of your app and its not related to JPA. So, I would copy the object before presisting it, or at least copy the image field alone. After presistance, assigning the ID of the presisted object to the ID of the copied object.
Or, another solution, you can add a string field to your database that will hold a path to your image; You start by writing the image to the 'heart content' or wherever, and then assign the path of the image to the entity and persist that path in the database.
Or, another solution, you can add another data model layer to your system above the entity layer, which contains the JSON classes, so you dont need to have a transient fields in your entity nor JSON annotations.
Related
I created multiple tables for car manufacturers,
AUDI, BMW, VOLKSWAGEN, [etc]
but I'm trying to approach saving entities to said tables simplest way possible, and instead of creating, for example, CarArticleBMW, CarArticleAUDI, CarArticleVOLKSWAGEN I created a single model CarArticle
public class CarArticle {
#Column(name = "MANUFACTURER")
private String manufacturer;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "SERIES")
private String series;
#Column(name = "GEN")
private String gen;
#Column(name = "PRICE")
private String price;
}
Is there a way to make a JPA Repository to save entities, based on manufacturer String passed within a request body, to specific tables?
So that upon sending a request with following data (I'll be using DTO without ID so we can generate id's)
{
"manufacturer":"BMW",
"series":"3",
"gen":"e36",
"price":"3600€"
}
The data will be sent into the BMW table?
Should I create a base CarArticleDTO and then create CarArticle[specific manufacturer] model classes that inherit data from DTO upon request, and basing on manufacturer, save the entity using specific entity model?
I have this entity, in which I have made a PUT and POST method, which do not give an error but nevertheless the idMoneda, which is a property calculated with #Transient because it is the ID of the moneda(where there is a 1 to 1 relationship with another table), it does not update me, when I look at the database it remains null even though in the POST request I put a value. I don't know if it's because the setter is wrong, or just that something else needs to be added that I don't see right now.
#Entity
#Table(name = "REMESA")
public class Remesa {
#Id
#SequenceGenerator(name = "remesa_sequence", sequenceName = "remesa_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "remesa_sequence")
#Column(name = "ID")
private Long id;
#OneToOne
#JoinColumn(name = "moneda", nullable = true)
#JsonIgnore
private Moneda moneda;
#Transient
#JsonProperty("moneda")
private Long idMoneda;
public Long getIdMoneda() {
return this.moneda.getId();
}
public void setIdMoneda(Long idMoneda) {
this.idMoneda = idMoneda;
}
}
#Transient in JPA means: do not save this field in DB. A column named "moneda_id" will automatically be generated by your relationship if it's well-defined
Java's transient keyword is used to denote that a field is not to be serialized, whereas JPA's #Transient annotation is used to indicate that a field is not to be persisted in the database, i.e. their semantics are different. Because they have different meanings.
So try to remove the transient annotation and run your code .
I have the following pair of entity classes:
#Entity(name="metadata")
public class Metadata {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// Note: Hibernate CascadeType.ALL is also being used elsewhere in this class, hence
// the fully qualified class name used below
#OneToMany(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "metadata")
private List<Attachment> attachments;
// getters and setters
}
#Entity(name="attachment")
public class Attachment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "metadata_id", referencedColumnName = "id", nullable = false, updatable = false)
private Metadata metadata;
// getters and setters
}
For completeness, here is how I am building the Metadata object:
Metadata metadata = modelMapper.map(req, Metadata.class);
List<Attachment> attachments = new ArrayList<>();
// the files come as a parameter to a Spring controller endpoint (FYI)
for (MultipartFile file : files) {
Attachment attachment = new Attachment();
attachment.setContents(file.getBytes());
attachment.setFilename(file.getOriginalFilename());
attachments.add(attachment);
}
metadata.setAttachments(attachments);
metadata.setDraft(isDraft);
myJPARepository.save(metadata);
What I observe when creating a Metadata entity and then calling save() from my JPA repository is that all data does get correctly written to my database (Postgres). However, the join column metadata_id is always NULL. At first, I thought this might have been caused due to the referencedColumnName attribute not being set (whose default is ""). However, adding this in as you see above did remedy the problem.
Does anyone know why the join column metadata_id is always appearing as NULL in the database table?
You need to synch both of your object, as of now you are creating metadata object and adding attachment to it and you have cascade so that will save both entities into their respective table.
But, since you have bidirectional relationship, you are only synching one side of relation ship here only, you need to set the same metadata object to each attachment object as well, then hibernate will be able to link the foreign key.
Instead of setter I would suggest use a add function on metadata object something like this
public void addAttachment(Attachment attachment) {
attachments.add(attachment);
attachment.setMetadata(this);
}
This way both the object would be in synch, use that inside in your for loop, you may have to initialise your collection inside metadata object before doing that or you can first check in above add function that if attachments list is null then create one and then add.
I have a Java Spring MVC app using Hibernate and a MySQL 5.7 DB. Hibernate is configured to automatically update the schema from new entities via: <prop key="hibernate.hbm2ddl.auto">update</prop>
It is working for all the entities in my app except for one:
#Entity
#IdClass(QuestionId.class)
#Getter
#Setter
#Accessors(chain = true)
#EqualsAndHashCode
public class Question {
#Id
private String key;
#Id
private Long version;
#Type(type = "json")
#Column(columnDefinition = "json")
private JsonNode metaData;
}
This entity is defined in exactly the same way as other entities, but when the app starts, this particular table does not get created in the DB. There are no error messages given that I have been able to uncover.
I actually discovered the problem with help from an answer to a different question: hibernate not creating table but no error messages
This behavior can happen when using reserved db keywords as property/column names. In my case, key is a reserved keyword in MySQL: https://dev.mysql.com/doc/refman/8.0/en/keywords.html
public class Question {
#Id
private String key; // <==== NO; `KEY` is a db reserved keyword.
#Id
private Long version;
#Type(type = "json")
#Column(columnDefinition = "json")
private JsonNode metaData;
}
Changing the property name (or alternatively specifying an explicit column name) fixes the issue. There may have been an error message if I enabled the right debugging flags, but the problem was solved before then.
I am using spring boot and jpa and i want to save data in table.
I have database table constraint not null on createdBy, updatedBy, updationDate, creatingDate columns.
I have similar DTO to entity and above mentioned audit fields are not in DTO but only in entity so meaning user don't send audit information.
On entity i am using this #EntityListeners(AuditingEntityListener.class).
Problem is when i try to update entity by rep.save(entity) i get this error "Column 'CreatedBy' cannot be null
My entity is using auditing entity listeners and it looks similar to this
#Column(name = "isactive")
private boolean active;
#CreatedBy
#Column(name = "createdby")
private String createdBy;
#CreatedDate
#Column(name = "creationdate")
private Instant creationDate;
#LastModifiedBy
#Column(name = "lastupdateby")
private String lastUpdateBy;
#LastModifiedDate
#Column(name = "lastupdatedate")
private Instant lastUpdateDate;
Note: If i try to create new object, it gets saved and worked fine and also audit information in database like created by updated by is also populated using auditing entity listener.
But when it try to update the same object i get the error of createdBy cannot be null, i am assuming these audit fields createdBy updatedBy .... should also be populated of filled by auditEntityListener the same way i create it by post request.
you can use the updatable = false flag, e.g.
#Column(name = "createdby", updatable = false)
and
#Column(name = "creationdate", updatable = false)
that helped me to solve this issue.
I got the solution, When we need to update the entity that is using AuditEntityListener
i updated only the changed fields in entity comparing to DTO.
MapStruct made it easy
void updateEntity(#MappingTarget Entity target, DTO dto);
now save the updated entity.
Have a look how to use Audit Aware in link