optimization hibernate - java

I have entity that use storage function. It's good, but some case I don't need in this fields (fieldStoreFunc1, fieldStoreFunc2..) and use Class B and fieldStoreFuncs only when it's necessary:
#Entity
#Table(name = "table")
#SequenceGenerator(name = "JDE_SEQUENCE", sequenceName = "JDE_SEQUENCE", allocationSize = 1)
public class EntityClass implements Trackable {
#Id
#GeneratedValue(generator = "JDE_SEQUENCE", strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "name_field")
private String field1;
#Column(name = "name_field")
private String field2;
#Column(name = "name_field")
private String field3;
......
//read only fields
#Formula("store_func(value)")
private String fieldStoreFunc1;
#Formula("store_func(value)")
private String fieldStoreFunc2;
#Formula("store_func(value)")
private String fieldStoreFunc3;
}
so can I divide class into to
class A{
#Column(name = "name_field")
private String field1;
#Column(name = "name_field")
private String field2;
.....
}
class B extends A{
//read only fields
#Formula("store_func(value)")
private String fieldStoreFunc1;
#Formula("store_func(value)")
private String fieldStoreFunc2;
#Formula("store_func(value)")
private String fieldStoreFunc3;
}

No, that's not how you should do.
First, measure, profile, and prove that loading these additional three fields causes a significant overhead and performance problem.
Once, and only once you have this proof, then consider lazy-loading the additional three fields as explained in the documentation. Then measure once again, and prove that the lazy-loading of these three fields does no cause an even bigger overhead and performance problem.
As stated in the documentation:
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note that
this is mostly a marketing feature; optimizing row reads is much more
important than optimization of column reads. However, only loading
some properties of a class could be useful in extreme cases. For
example, when legacy tables have hundreds of columns and the data
model cannot be improved.
Unless the store_func formula is very expensive, you probably should avoid trying to optimize anything.

Although I agree with JB Nizet if you are still willing to load those properties on demand. And just for the record.
You can achieve in a few ways. The more simplistic one is to create a constructor in your class with the property you always want to load
Please Note that is just an example and is not properly developed
public class A {
...
#Column(name = "name_field")
private String field1;
#Formula("store_func(value)")
private String fieldStoreFunc1;
...
public A(String id, String field1) {
this id = id;
this.field1 = field1;
}
And then in the DAO create a function that loads entities like
public class DAO {
getLight(Long id) {
String named_query = "select a.id, a.field1 from A a where a.id = :id"
// Execute query
}
Another way is to take a look at the Hibernate event or write a custom entity loader.

Related

Group and sort a collection of collections using JPA

I am looking to make a REST controller that will return a sorted list of various objects.
I have created a DTO to hold these collections like the following, but this will not work as it will group by entity:
public class AllReportsDTO {
private List<AReport> aReports;
private List<BReport> bReports;
private List<CReport> cReports;
...
}
I then have the following Domain objects
#Entity
#Table(name = "a_report")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class AReport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "description")
private String description;
#Column(name = "time_of_report")
private ZonedDateTime timeOfReport;
And one for each Report.
What I want to do is create an endpoint that will return a list of all these reports but in order of time of the report and not grouped by report. How can I achieve this?
I have tried writing it in the repository with a HQL query and grouping by time, but the issue is that each time field has a different name in each report which I can not alter due to this system being used in other places.
You can create methode that sort your sets. Try to adept this one
Collections.sort( aReports, new Comparator<Object>() {
public int compare(MyObject o1, Object o2) {
return o1.getTimeOfReport().compareTo(o2.getTimeOfReport());
}
});
I wouldn't try a pure HQL solution, or a solution from your ORM. I would go to the Java way.
Add an interface
public interface ITimedReport {
ZonedDateTime getTime();
}
Make all your report class implements this interface by returning their own timestamp
Add a method getAllReports on AllReportsDTO.
This method should fill a List<ITimedReport> with all reports, and then sort the list with a Comparator<ITimedReport>. This comparator would rely on the getTime() to compare.
You can add anything meaningfull for a report in the interface, like a getTitle, getDescription, ...

HQL strategy to equal embedded entities

i'm curious about how HQL would assert equality between an entity instances.
Let's say I have a Entity called Person
#Entity
public class Person{
#Id
private Long id;
private String name;
}
and Department
#Entity
public class Department {
#Id
private Long id;
#ManyToOne
private Person person;
}
then it's fine if I do the following statement:
Query query = getSession().createQuery("from Department d where d.person = ?");
query.setProperty(0,new Person(1L));
but, what if I have an Embedded entity and no pk defined? like
#Embeddable
public class Adress {
private String email;
private String street;
private Long identifier;
}
#Entity
public class Person{
#Id
private Long id;
private String name;
#Embedded
private Address address;
}
would have any way so I could tell JPA to make it work:
Query query = getSession().createQuery("from Person p where p.address = ?");
query.setProperty(0,new Address(1L));
even though it's not exactly a primary key?
For sure i know i'd work if I tried p.adress.identifier, and then passed just the Long value, but the point is, can I tell JPA provider how it's gonna kind of 'implement' equality my way?
Thank you all
No, it is not supported and it would be difficult in general or would not make sense in some situations, like when there are collections in the Embeddable.
If you find that you need this often though, consider converting such Embeddables to custom user types. Then you can perform comparisons the way you described.

Problems in mapping objects between the model and DTO

I am mapping between the following models:
#Entity
#Table(name="account_type")
#NamedQuery(name="AccountType.findAll", query="SELECT a FROM AccountType a")
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="account_type_id")
private Integer accountTypeId;
#Column(name="account_type_code")
private String accountTypeCode;
#OneToMany(mappedBy="accountType")
private Set<Account> accounts;
Which has a set of Account:
#Entity
#NamedQuery(name="Account.findAll", query="SELECT a FROM Account a")
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="account_id")
private Integer accountId;
#Column(name="account_number")
private String accountNumber;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="account_type_id_fk")
private AccountType accountType;
And their DTOs.
I am having problems in mapping complex types like Account:
public static Account getAccount(AccountDTO dto) {
Account model = new Account();
model.setAccountId(dto.getAccountId());
model.setAccountNumber(dto.getAccountNumber());
model.setAccountType(dto.getAccountType());
// Error: can't convert from AccountypeDTO to AccountType
return model;
}
It gives an error that it can't convert from AccountypeDTO to AccountType
so I did the following:
model.setAccountType(getAccountType(dto.getAccountType()));
Where getAccountType method is:
public static AccountType getAccountType(AccountTypeDTO dto) {
AccountType model = new AccountType();
model.setAccountTypeId(dto.getAccountTypeId());
model.setAccountTypeCode(dto.getAccountTypeCode());
model.setAccounts(dto.getAccounts());
// Now here again a similar error
}
I think it's a deep recursive? How to solve this?
My question is how to convert them efficiently.
Annex
The code of acountTypeDTO:
#Component
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class AccountTypeDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotEmpty
private Integer accountTypeId;
#NotNull
#NotEmpty
private String accountTypeCode;
private Set<AccountDTO> accounts;
The code of AccountDTO:
#Component
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class AccountDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotEmpty
private Integer accountId;
#NotNull
#NotEmpty
private String accountNumber;
private AccountTypeDTO accountType;
Two alternative approaches jump to mind but they will require some change. I will preface this by saying that I have yet to be in a situation where converting to DTOs (even if I'm doing a deep recursive conversion) is the bottleneck. Even if your performance requirements or your scale were so large that it somehow did become a bottleneck then I would personally recommend dividing the work across multiple servers before I started to worry about performance down to that level of detail. Also, it may seem inefficient but performance is rarely intuitive, have you confirmed that this conversion is a bottleneck?
The first alternative is to not use separate classes as DTOs. Some approaches use the same class as the DTO and the underlying entities and some approaches use the DTO as the parent class and the entity class as the child class. This will save you from having to do any kind of DTO<->Entity conversion. There are drawbacks, as this almost always ends up combining two responsibilities into a single class and it can make your code more complex and less readable.
The second alternative is to not return the accounts themselves but instead to convert them to IDs. In this approach your AccountTypeDTO would have a Set<Integer> accountIds instead of a Set<AccountDTO> accounts. However, this only works if your client doesn't always need to operate on every account.

JPA ManyToMany join table query

Assuming theses Entities
#Entity
public class EntityNote implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="SeqEntityNote", sequenceName="SeqEntityNote", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityNote")
private long id;
private Date date;
private String subject;
private String content;
#ManyToMany
private List<EntityTopic> listEntityTopic;
//setters/getters
#Entity
public class EntityTopic implements Serializable {
#Id
#SequenceGenerator(name="SeqEntityTopic", sequenceName="SeqEntityTopic", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SeqEntityTopic")
private long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
In my DB, a join table named "entity_note_list_entity_topic" records the ManyToMany relation.
This works correctly so far.
But I'd like to perform a count query like 'how many EntityNotes per EntitityTopic'
Unfortunatly I'm quite lost in this situation.
How this query can be written ?
Do I need other elements in my two entities ?
(In many examples I see a reverse relation using mappedBy attribute on ManyToMany.. Do I need this ?)
It will be the easiest if you make the many to many relation bidirectional. There are no serious extra costs involved, as it uses the same db structure, and the list are lazy loaded so if the relation is not being used the lists are not populated (you can hide the second direction by making accessors private).
Simply change:
#Entity
public class EntityTopic implements Serializable {
...
#ManyToMany(mappedBy="listEntityTopic")
private List<EntityNote> notes;
}
You can issue normal count jpql queries, for example:
SELECT count(n) from EntityTopic t INNER JOIN t.notes n where t.name =:name
so you don't neet to retrieve the notes and topics if don't need to.
But I also believe that your original mapping can also be queries with:
SELECT COUNT(n) FROM EntityNote n INNER JOIN n.listEntityTopic t WHERE t.name = :name
If you have the following code:
#Entity
public class EntityNote implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityTopic> topics;
}
#Entity
public class EntityTopic implements Serializable {
#ManyToMany(fetch = FetchType.LAZY)
private List<EntityNote> notes;
}
Then, topic.getNotes().size() will give you the number of notes associated with a topic. When using Hibernate as the JPA provider, a SELECT COUNT(...) query is issued for this instead of loading all the associated notes. If this does not work for you out-of-the-box, mark the collections as extra lazy using the instructions in this post.

JPA: Foreign key that is also a primary key mapping

I have been trying to solve this for whole day but no luck! Also i tried to read most of the tutorials on the net but as you all know they all are full of useless examples that do not reflect what you need in the real world.
So here is my situation:
The database:
table: vehicles(vehicleId, brand, model, devYear, regNumber) <-- vehicleId is the PrimaryKey
table: extras(vehicleId, allowSmoke, allowFood, allowDrinks, airConditioner) <-- vehicleId is a PK and a FK.
The point is that if i have a class Vehicle and a class TravelExtras which are mapped to the database i want the Vehicle class to have an attribute TravelExtras travelExtras and get and set methods.
Unfortunatelly no matter what i tried when i try to persist the object in the databse i get various errors.
Here is an illustration:
EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "NaStopPU" );
EntityManager entitymanager = emfactory.createEntityManager( );
entitymanager.getTransaction( ).begin( );
TravelExtra travelExtra = new TravelExtra();
entitymanager.persist(travelExtra);
Vehicle vehicle = new Vehicle(2L, "10152487958556242", "Mazda", "626", "334343", 2005, 4);
vehicle.setTravelExtra(travelExtra);
entitymanager.persist(vehicle);
entitymanager.getTransaction().commit();
entitymanager.close( );
emfactory.close( );
Any one knows what kind of annotations to use for this One to one case ?
The Java Persistence wikibook has a section called Primary Keys through OneToOne and ManyToOne Relationships which seems to indicate that what you want is possible.
If I'm reading it right, for your case, it would look something like:
class Vehicle {
#Id
#Column(name = "EXTRAS_ID")
private long extrasId;
#OneToOne(mappedBy="vehicle", cascade=CascadeType.ALL)
private TravelExtra extras;
}
class TravelExtras {
#Id
#Column(name = "VEHICLE_ID")
private long vehicleId;
#OneToOne
#PrimaryKeyJoinColumn(name="VEHICLE_ID", referencedColumnName="EXTRAS_ID")
private Vehicle vehicle;
public TravelExtras(Vehicle vehicle) {
this.vehicleId = vehicle.getId();
this.vehicle = vehicle;
}
}
Note that one of your entities will need to make sure it has the same id as the other, which is accomplished in the example by the TravelExtras constructor requiring the Vehicle it is bound to.
I know this is very old qs, but for completeness of your case
you can just have (jpa 2.0)
#Entity
#Data
public class Vehicle implements Serializable{
#Id
#GeneratedValue
private long vehicleId;
.. //other props
}
#Entity
#Data
public class VehicleExtras implements Serializable{
#Id
#OneToOne (cascade = CASCADE.ALL)
#MapsId
#JoinColumn(name ="vehicleId")
private Vehicle vehicle;
#Column
private boolean allowSmoke;
..// other props.
}
should share same pk/fk for VehicleExtra table
Why don't you use an #Embedded object? When using an embedded object, you get
the logical separation you desire in your code and keep your database compliant with Entity-Relational Normalization rules.
It's weird to think on a One-to-One relationship, because even though JPA/Hibernate allows it, all data should be stored in the same table, making you model simpler, while also simplifying queries and increasing database performance by removing the need for a Join operation.
When using Embedded objects you don't have to worry about mapping IDs and bizarre relations, since your ORM is capable of understanding that your just making a code separation, instead of demanding an actual relation of One-to-One between tables.
class Vehicle {
#Id
#Column(name = "ID")
private long vehicleId;
#Column(name = "BRAND")
private String brand;
#Column(name = "MODEL")
private String model;
#Column(name = "DEV_YEAR")
private int devYear;
#Column(name = "REG_NUMBER")
private int regNumber;
#Embedded
private TravelExtra extras;
// Constructor, getters and setters...
}
.
#Embeddable
class TravelExtras {
#Column(name = "ALLOW_SMOKE")
private boolean allowSmoke;
#Column(name = "ALLOW_FOOD")
private boolean allowFood;
#Column(name = "ALLOW_DRINKS")
private boolean allowDrinks;
#Column(name = "AIR_CONDITIONER")
private boolean airConditioner;
// Default Constructor, getters and setters...
}
You can map your classes for example with Netbeans. It will generate annotations. The problem could be your dao layer. You have to persist objects in correct way. For example can't save travelExtra without Vehicle. Also be aware of owning side.

Categories