Hibernate - How to persist an entity but disregarding certain fields? - java

I have an Entity with several fields:
#Entity
public class Leilighet {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long leilighetId;
private String navn;
private String beskrivelse;
private String adresse;
private String postNr;
private String postSted;
private double pris;
//The following fields should not be stored:
private String filename;
private String filetype;
private String filesize;
private byte[] filebin;
....
}
I have a corresponding form and Action that will populate this object and persist it to mySql.
This is all good when it comes to storing new "Leilighet"-entities.
But when im using this form to "edit" an existing "Leilighet", I have stumbled upon something i cant figure out what to do.
For editing purposes i do not want to load the entire uploaded file. It is enough to just show the filename to indicate that there is a file present. If the user chooses a new File then it should be overwritten, but if the user chooses nothing then the present file should be kept.
But I cant figure out how to make hibernate do what i want. Unless i populate the filebin with the actual file, hibernate will just delete the file.
How can I tell hibernate to just update the other fields and not the filefields?

Hibernate supports loading individual properties lazily. See http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#performance-fetching-lazyproperties for details.
Or you could just use a DTO with only the fields you want in this specific use-case, and execute a query with the appropriate projections in order to populate this DTO from the database.

Related

Question about Springboot, mongoDB relations and API

So I'm trying to create a project based in some weather forecast API and the data comes in different objects like:
public class WeatherForecast{
private String local;
private int maxTemp;
private int minTemp;
private int precipitationId;
}
public class Precipitation{
#Id
private int id;
private String descriptionEn;
private String descriptionCh;
...
}
So, I want to store this information and obtain the description of the Precipitation in the language that I desire, without receiving an WeatherForecast with the description of 10+ languages.
I searched a lot about defining relations between the tables but I couldn't find something related to relationships by id's. I found that I could use,
#DbRef
private Precipitation precipitation;
but I couldn't understand how it can interpret the Id without passing in the constructor.
And, finally, I want to understand if it's a good practice to send the two objects
separately to the database and try to workout some functionality to get the object I want, something like this:
public class WeatherForecast{
private String local;
private int maxTemp;
private int minTemp;
private String description;
}
using criteria, queries, or other things without extracting the two tables and searching in Precipitations for the desired description language.

How to create an entity object in Spring Boot when the entire table is the key?

I'm creating an entity object that I will use to query a database with. However, I'm not sure how to build it since all of the columns are the key for the table. Typically I'd create an Entity object with the parameters that aren't the key and make a nested object within that one as the key. That entity object key would hold the values needed to build the key. But, in this case, all 5 values of the table are used for the key.
This is what I have currently, but I know that it's wrong.
Would I just have to make an #EmbeddedId key object within this one and push all of the values to that while practically leaving this base entity object empty? Or is there a better way to do this?
#Entity
#Table(name = "<insert>", schema="<insert>")
public class Market {
#Id
#Column(name="MKT_ID")
private String marketId;
#Column(name="DLR_CD")
private String dealerCd;
#Column(name="SER_NO_PFX")
private String serialNoPrefix;
#Column(name="SER_NO_BDY")
private String serialNoBody;
#Column(name="USER_ID")
private String userId;
}

Hibernate #SQLDelete sql not adding schema

I am trying to use the #SQLDelete annotation of Hibernate to make soft deletion. It works well when the DB schema is static, i.e: passing it in the SQL.
Unfortunately, it seems the SQL is passed as is to EntityPersisters (cf EntityClass's method CustomSQL createCustomSQL(AnnotationInstance customSqlAnnotation) so I can't find a way to pass the schema name dynamically like in Native SQL queries using {h-schema}
Did anyone find a good workaround for this issue (I am using Hibernate 4.3.5)?
Edit: Unless there is a real solution, I ended up modifying the code source of org.hibernate.persister.entity.AbstractEntityPersister by replacing the schema placeholder when setting the custom SQL queries in method doLateInit.
Edit2: I have created an issue for this behaviour in Hibernate JIRA. I will create a pull request later today and I wish the Hibernate Team will accept it
Soft deletes using Hibernate annotations.
As linked author stated below:
I am currently working on a Seam application that has a need for soft deletes in the database. To the right you can see a snippet of my database diagram which contains a CUSTOMER and APP_USER table. This is just a straight forward one to many relationship but the important thing to note though is the “DELETED” field in each table. This is the field that will be used to track the soft delete. If the field contains a ‘1’ the record has been deleted and if it contains a ‘0’ the record hasn’t been deleted.
Before ORMs like Hibernate I would have had to track and set this flag myself using SQL. It wouldn’t be super hard to do but who wants to write a bunch of boilerplate code just to keep track of whether or not a record has been deleted. This is where Hibernate and annotations comes to the rescue.
Below are the 2 Entity classes that were generated by Hibernate using seamgen. I have omitted parts of the code for clarity.
Customer.java
//Package name...
//Imports...
#Entity
#Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
private Billing billing;
private String name;
private String address;
private String zipCode;
private String city;
private String state;
private String notes;
private char enabled;
private char deleted;
private Set appUsers = new HashSet(0);
// Constructors...
// Getters and Setters...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "customer")
// Filter added to retrieve only records that have not been soft deleted.
#Where(clause = "deleted <> '1'")
public Set getAppUsers() {
return this.appUsers;
}
public void setAppUsers(Set appUsers) {
this.appUsers = appUsers;
}
}
AppUser.java
//Package name...
//Imports...
#Entity
#Table(name = "APP_USER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE app_user SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class AppUser implements java.io.Serializable {
private long id;
private Customer customer;
private AppRole appRole;
private char enabled;
private String username;
private String appPassword;
private Date expirationDate;
private String firstName;
private String lastName;
private String email;
private String phone;
private String fax;
private char deleted;
private Set persons = new HashSet(0);
// Constructors...
// Getters and Setters...
}
The following 2 steps is all that I had to do to implement the soft delete.
Added the #SQLDelete annotation which overrides the default
Hibernate delete for that entity.
Added the #Where annotation to filter the queries and only return
records that haven’t been soft deleted. Notice also that in the
CUSTOMER class I added an #Where to the appUsers collection. This is
needed to fetch only the appUsers for that Customer that have not
been soft deleted.
Viola! Now anytime you delete those entities it will set the “DELETED” field to ‘1’ and when you query those entities it will only return records that contain a ‘0’ in the “DELETED” field.
Hard to believe but that is all there is to implementing soft deletes using Hibernate annotations.
Note:
also note that instead of using the #Where(clause="deleted ‘1’") statements you can use hibernate filter (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-filters) to globally filter-out all ‘deleted’ entities. I found that defining 2 entity managers (‘normal’ one that filter deleted items, and one that doesn’t, for the rare cases…) is usually quite convenient.
Using EntityPersister
You can create a DeleteEventListener such as:
public class SoftDeleteEventListener extends DefaultDeleteEventListener {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void onDelete(DeleteEvent event, Set arg1) throws HibernateException {
Object o = event.getObject();
if (o instanceof SoftDeletable) {
((SoftDeletable)o).setStatusId(1);
EntityPersister persister = event.getSession().getEntityPersister( event.getEntityName(), o);
EntityEntry entityEntry = event.getSession().getPersistenceContext().getEntry(o);
cascadeBeforeDelete(event.getSession(), persister, o, entityEntry, arg1);
cascadeAfterDelete(event.getSession(), persister, o, arg1);
} else {
super.onDelete(event, arg1);
}
}
}
hook it into your persistence.xml like this
<property name = "hibernate.ejb.event.delete" value = "org.something.SoftDeleteEventListener"/>
Also, don't forget to update your cascades in your annotations.
Resource Link:
Hibernate: Overwrite sql-delete with inheritace
Custom SQL for CRUD operations
Custom SQL for create, update and delete
Use like this
#SQLDelete(sql = "UPDATE {h-schema}LEAVE SET STATUS = 'DELETED' WHERE id = ?", check = ResultCheckStyle.COUNT)
I think there are 2 way
First is to add:
app.datasource.schema=<schema_name>
to your application.properties.
The second is to use the schema in annotation to your table model

OrientDB: delete record and references created with Object API

I am using the OrientDB 2.1.6 Object API.
I have two POJOs with a 1 to N relationship like this:
public static class Results {
private String userId;
private String templateId;
private Double totalLength;
private List<String> visibleFields;
private Boolean filterable;
#OneToMany(orphanRemoval = true)
private List<ResultItem> items;
//Generic getters and setters
}
public static class ResultItem {
private String id;
private String vsId;
private String entryTemplateId;
private String objectType;
private String objectTypeLabel;
private String capabilityComment;
private Boolean currentVersion;
private Double contentSize;
private String name;
private String objectStoreId;
private String mimeType;
private HashMap<String, String> attributes;
private Date dateLastModified;
//generic getters and setters
}
This creates two classes in OrientDB. If I delete a Results instance using the Object API, it will delete associated ResultItem rows correctly.
I am trying to delete a particular ResultItem record using the "console" like this:
orientdb {db=test}> find references #15:6392 Found
[{rid:#15:6392,referredBy:[1]}] in 0.014000 sec(s).
orientdb {db=test}> delete from ResultItem where #rid=#15:6392 Delete
record(s) '1' in 0.006000 sec(s).
orientdb {db=test}> find references #15:6392 Found
[{rid:#15:6392,referredBy:[1]}] in 0.014000 sec(s).
The console output suggests that the record has been deleted but it continues to contain a "reference".
This manifests itself as a problem when I go back to the Object api and try to db.detachAll(results, true);. It throws this exception which I assume is due to the the orphan relationship.
Caused by: java.lang.NullPointerException
at com.orientechnologies.orient.object.db.OObjectLazyList.convertAndDetachAll(OObjectLazyList.java:456)
at com.orientechnologies.orient.object.db.OObjectLazyList.convertAndDetachAll(OObjectLazyList.java:432)
at com.orientechnologies.orient.object.db.OObjectLazyList.detachAll(OObjectLazyList.java:424)
at com.orientechnologies.orient.object.enhancement.OObjectProxyMethodHandler.detachAll(OObjectProxyMethodHandler.java:165)
at com.orientechnologies.orient.object.enhancement.OObjectEntitySerializer.detachAll(OObjectEntitySerializer.java:261)
at com.orientechnologies.orient.object.db.OObjectDatabaseTx.detachAll(OObjectDatabaseTx.java:809)
at com.orientechnologies.orient.object.db.OObjectDatabaseTx.detachAll(OObjectDatabaseTx.java:327)
How can I delete the relationship along with the record?
I tried your case and I got your same results.
This is a limitation about the references because there's no check on #RID consistency and when you delete a document, the deletion of all references would activate a full scan of the DB to search all the documents linked to the first and then drop the references.
This would be a very expensive operation and it would take a lot of time, this is one the reasons because using edges is reccomended instead of the LINKS, LINKLIST,...
Hope it helps

JDBC Domain Design and Relationships

I've used Hibernate / JPA in the past, now using a combination of Spring JDBC and MyBatis.
With JPA/ Hibernate if you had a Customer, which had an address you would have a domain structure similar to code below. (minus all the annotations / config / mappings).
Does this still make sense when using JDBC or MyBatis. This is composition domain design from what I know, has-a, belongs-to, etc. However most examples I've seen of JDBC code they have domain object that bring back the IDs rather than collection, or flatten the data. Are there any performance benefits from either approach, maintainability, etc. Having worked with JPA first I'm not sure what the JDBC way of doing things are.
public class Customer {
private Long id;
private String userName;
private String password;
private String firstName;
private String lastName;
private Collection<Address> addresses
...
}
public class Address {
private Long id;
private String streetAddress1;
private String streetAddress2;
private String city;
private State state;
private String postalCode;
}
public class State {
private Long id;
private String code;
private String name;
private Country country;
}
public class Country {
private Long id;
private String code;
private String name;
}
I come across an example and here was one of their classes.
public class Question {
private long questionId;
private long categoryId;
private long userId;
private long areaId;
private String question;
private String verifyKey;
private Date created;
private User user;
private List<Answer> answers;
private long answerCount;
private String name;
// getters and setters omited...
}
Why would you fetch the userId, areaId, and categoryId instead of actually fetching the associated object? The ID is likely of no use to the front end user, I suppose you could use the ID to issue another query to fetch additional data, but seems inefficient making another round trip to the database.
You can look at this domain object as a "footprint" of database table. In your example, userId, areaId and categoryId from Question are most likely foreign keys from corresponding tables. You could never need full object data in the moment of Question creation and retrieve it later with separate db request. If you fetch all associated objects at once, you will hit at least one additional table per object (by join-s or subselect-s). Moreover, that's actually the same that Hibernate does. By default, it loads domain object lazily and hits database again if uninitialized associated object is needed.
At that time, it is better to fetch those objects that a domain object can't exist without. In your example, Question and List are coupled.
Of course, if you need user, or category, or any other associated object again in some another place of application (assume the reference to previously retrieved object has been lost), you will hit the database with same query. It should be done and could seem inefficient, because both plain JDBC and SpringJDBC have no intermediate caching unlike Hibernate. But that's not the purpose JDBC was designed for.

Categories