Hibernate with SqlServer performance drop on select - java

I have a performance issue with the following Hibernate TypedQuery:
select generatedAlias0 from MyClass1 as generatedAlias0
where generatedAlias0.class2.uid in (:param0, ..., :paramN)
Following the actual implementation this results in the following query:
select myclass1_.id, myclass1_.other, ... # 16 fields in total, no special big ones
from myschema.dbo.TblMyClass1 myclass1_ cross join myschema.dbo.TblMyClass2 myclass2_
where myclass1_.myclass2Id=myclass2.Id and (myclass2_.uid in('value1', ... 'valueN'))
Where N each time stands for 384 items.
When I execute this query in Toad it only takes about 150ms, but executed from code it takes almost a minute!
Class mappings
#Entity
#Table(name = "TblMyClass1", catalog = "myschema", schema = "dbo")
public class MyClass1 implements Serializable {
private static final long serialVersionUID = 8208493383744288872L;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
#NotNull
#ManyToOne
#JoinColumn(name = "myclass2Id", referencedColumnName = "Id", nullable = false)
private MyClass2 class2 = null;
#NotNull
#ManyToOne
#JoinColumn(name = "myclass3Id", referencedColumnName = "Id", nullable = false)
private MyClass3 class3 = null;
#NotNull
#ManyToOne
#JoinColumn(name = "myClass4Id", referencedColumnName = "Id", nullable = false)
private MyClass4 myClass4;
#ManyToOne
#JoinColumn(name = "myClass5Uid", referencedColumnName = "UID", nullable = true)
private MyClass5 resultType;
#Column(name = "string2", nullable = true)
private String string2;
// other column fields and getters and setters ...
}
#Entity
#Table(name = "TblMyClass2", schema = "myschema", catalog = "dbo")
public final class MyCLass2 implements Serializable {
private static final long serialVersionUID = 4660579327140751989L;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
#Column(name = "uid", nullable = false, updatable = false, unique = true)
private String uid;
// ... other columns fields and getters and setters
}
I only provided the mappings for the two classes in the slow query, since the follow-up queries of hibernate which fill all other linked entities all perform very good.
Using Java VisualVm I find that the following method takes 99% of the time:
com.microsoft.sqlserver.jdbc.TDSChannel.read()
I am running SQL Server 2008 and using sqljdbc4-2.0 and hibernate4.2.1 with spring3.2.9 (for transaction management)
Any help would be greatly appreciated !

Related

Spring Boot JPA - ManyToOne relationship causes extra sql

I have 3 objects with simple relationship which looks as follows:
University:
#Entity
public class University {
#Id
#GeneratedValue
private Long id;
private String name;
}
Faculty:
#Entity
public class Faculty {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "university_id", nullable = false)
private Long universityId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinFetch(value = JoinFetchType.OUTER)
#JoinColumn(name = "university_id", insertable = false, updatable = false, nullable = false)
private University university;
}
Specialty:
#Entity
public class Specialty {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "faculty_id", nullable = false)
private Long facultyId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinFetch(value = JoinFetchType.OUTER)
#JoinColumn(name = "faculty_id", insertable = false, updatable = false, nullable = false)
private Faculty faculty;
}
I am using EclipseLink and Spring CrudRepository to operate with these entities.
When i call
specialtyRepository.findAll();
i get sql
SELECT * FROM specialty LEFT OUTER JOIN faculty ON (faculty.ID = specialty.faculty_id) ...
and extra sql like
SELECT * FROM university WHERE ((ID = ?)) ...
I want to prevent this sql request;
Could someone tell me how to resolve this issue?
Thank you for any tips
For lazy loading in #ManyToOne i must enable dynamic weaving in EclipseLink:
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving
I solved this problem via inheritance;
I have created the base instance without relationship;
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class FacultyBase {
#Id
#GeneratedValue
private Long id;
private String name;
#Column(name = "university_id", nullable = false)
private Long universityId;
}
and instance with relationship
#Entity
public class Faculty extends Faculty {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "university_id", insertable = false, updatable = false, nullable = false)
private University university;
}
As a result, in Specialty instance i use FacultyBase instead of Faculty

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

eclipselink SQLServerException inserting into a join table with additional column

I have a many-to-many relationship which looks like this:
The primary key is combination of three columns and I'm using eclipselink. I created these classes to be able to insert in the join-table :
#Entity
#Table(name = "definition_property")
#NamedQueries({
#NamedQuery(name = "DefinitionProperty.findAll", query = "SELECT d FROM DefinitionProperty d")})
public class DefinitionProperty extends AbstractEntity{
private static final long serialVersionUID = 1L;
#EmbeddedId
protected DefinitionPropertyPK pk;
#JoinColumn(name = "dtid", referencedColumnName = "id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private DefinitionType definitionType;
#JoinColumn(name = "prid", referencedColumnName = "id", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Property property;
#Column(name = "initial_value")
#Basic(optional = false)
private String initialValue;
// setters and getters
}
And PK class:
#Embeddable
public class DefinitionPropertyPK implements Serializable{
#Column(name = "dtid")
private Integer idDt;
#Column(name = "prid")
private Integer idProperty;
#Column(name = "initial_value")
private String initialValue;
//hashcode, equals, setters and getters
}
Entitiy 1:
#Entity
#Table(name = "definition_type")
public class DefinitionType extends AbstractEntity implements EntityItem<Integer> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer idDT;
#Size(max = 45)
#Column(name = "name")
private String dtName;
#Size(max = 45)
#Column(name = "description")
private String description;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "definitionType")
private List<DefinitionProperty> definitionProperties = new ArrayList<DefinitionProperty>();
}
Entity 2:
#Entity
#Table(name = "property")
public class Property extends AbstractEntity implements EntityItem<Integer>{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
#Basic(optional = false )
#Column(name = "id")
private Integer idProperty;
#Column(name = "name")
#Size(max = 45)
private String propertyName;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private Type fieldType;
#Column(name = "init_value")
#Size(max = 45)
private String initialValue;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "property")
private List<DefinitionProperty> definitionPeoperties= new ArrayList<DefinitionProperty>();
}
Exception : I get this exception when trying to persist a new DefinitionType:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: The column name 'initial_value' is specified more than once in the SET clause. A column cannot be assigned more than one value in the same SET clause. Modify the SET clause to make sure that a column is updated only once. If the SET clause updates columns of a view, then the column name 'initial_value' may appear twice in the view definition.
Error Code: 264
Call: INSERT INTO definition_property (initial_value, initial_value, dtid, prid) VALUES (?, ?, ?, ?)
bind => [4 parameters bound]
Question : Why there are two initial_values in the set clause and where am I wrong in my code?
Ref: How do you Set Up a Many-to-Many Relationship with Junction Table using JPA/EclipseLink
Ref: JPA simple many-to-many with eclipselink
Ref: https://giannigar.wordpress.com/2009/09/04/mapping-a-many-to-many-join-table-with-extra-column-using-jpa/

Java Persistence Query Language JOIN

I have two tables :
A(bigint id, ...)
B(bigint id, varchar name, bigint id_A)
and now I want get all rows from A which exists in B (and those rows in B have name eg Andy)
Plase help me create dynamic query
class A
#Entity
#Table(name = "A", schema = "mySchema")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class A{
#Id
private Long id;
}
class B
#Entity
#Table(name = "B",
schema = "mySchema",
uniqueConstraints = { #UniqueConstraint(columnNames = {
"some_id", "id_A" }) })
public class B{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "Seq")
#SequenceGenerator(name = "Seq", sequenceName = "mySchema.mySeq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
}
There are several weird parts. e.g. this:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
With the #JoinColumn annotation you are telling the JPA provider that it should use the specified column for internal mapping, but with the IdA field, you are trying to manage the column yourself. Which is it going to be?

Hibernate MS SQL Join issue

I have two tables in the clients mssql database. The first is a job table - so I created an Job entity which contains the load type and load weight and all that stuff - works fine.
My problem now is that there is a second table that includes informations about the load and unload point. The second table, I call it JEP, has a primary key consisting of several items: the type (load or unload), the zip code and the customer number.
I created an entity JobEndPoint and NetBeans also created an object representing the primary key JobEndPointPK containing all that fields.
I want to add two JobEndPoint (loadPoint and unloadPoint) to my Job entity. My problem is now: how do I annotate that in Hibernate? In my opinion it is an #OneToOne relation ship. It would be perfect if I could specify a SELECT statement like SELECT * FROM JEP WHERE type="load" AND customer_nr="123" AND zip_code="123 ...". Is that possible with Hibernate?
Thanks for your help!
Regeards,
Marco
Here are the Entities:
#Entity
#Table(name = "Auftragsdaten", catalog = "...", schema = "dbo")
public class Job implements Comparable<Object>, Serializable {
private static final long serialVersionUID = 4285871251915951149L;
#Id
#Basic(optional = false)
#Column(name = "`id`", nullable = false)
int id;
#Column(name = "`AufNr`", nullable=false)
int jobId;
#Transient
List<Integer> jobsAdded;
#Column(name = "`Beladedatum`", nullable=false)
#Temporal(TemporalType.DATE)
Date loadDate;
#Column(name = "`Beladezeit`")
#Temporal(TemporalType.TIME)
Date loadTimeFrom;
#Transient
Date loadTimeTo;
#Column(name = "`Entladedatum`", nullable=false)
#Temporal(TemporalType.DATE)
Date unloadDate;
#Column(name = "`Entladezeit Beginn`")
#Temporal(TemporalType.TIME)
Date unloadTimeFrom;
#Column(name = "`Entladezeit Ende`")
#Temporal(TemporalType.TIME)
Date unloadTimeTo;
#Transient
List<JobEndPoint> froms;
#OneToOne
#JoinColumns ({
#JoinColumn(name="`Beladetyp`", referencedColumnName = "`Ladetyp`", insertable = false, updatable = false),
#JoinColumn(name="`AbsNr`", referencedColumnName = "`KundenNr`", insertable = false, updatable = false),
#JoinColumn(name="`Verkehrsart`", referencedColumnName = "`VerkArt`", insertable = false, updatable = false),
#JoinColumn(name="`von LKZ`", referencedColumnName = "`LKZ`", insertable = false, updatable = false),
#JoinColumn(name="`von PLZ`", referencedColumnName = "`PLZ`", insertable = false, updatable = false)
})
JobEndPoint fromPoint;
#Transient
JobEndPoint toPoint;
#Column(name = "`Verkehrsart`", length = 10, nullable=false)
#Enumerated
JobType type;
#Column(name = "`Anzahl Paletten CCG1`")
int numberCCG1;
#Column(name = "`Anzahl Paletten CCG2`")
int numberCCG2;
#Transient
int numberFullContainer;
#Transient
int numberEmptyContainer;
#Column(name = "`Anzahl Container`")
int numberContainer;
#Column(name = "`Anz Stellplätze`")
int numberUnits;
#Column(name = "`Bruttogewicht`", nullable=false)
int loadWeight;
#ManyToOne
#JoinColumn(name="`Kühlkennzeichen`")
CoolingCode coolingCode;
}
#Entity
#Table(name = "BES", catalog = "...", schema = "dbo")
public class JobEndPoint implements Serializable {
private static final long serialVersionUID = 1017986852824783744L;
#Id
protected JobEndPointPK jobEndPointPK;
(...)
}
#Embeddable
public class JobEndPointPK implements Serializable {
#Basic(optional = false)
#Column(name = "`Ladetyp`", nullable = false, length = 50)
#Enumerated
EndPointType type;
#Basic(optional = false)
#Column(name = "`KundenNr`", nullable = false)
int customerId;
#Basic(optional = false)
#Column(name = "`VerkArt`", nullable = false, length = 10)
#Enumerated
JobType jobType;
#Basic(optional = false)
#Column(name = "`LKZ`", nullable = false, length = 3)
String countryCode;
#Basic(optional = false)
#Column(name = "`PLZ`", nullable = false, length = 7)
String zipCode;
}
In general, I would recommend using a generated internal primary key instead of the composite key. However, if you need to stick with your composite key, here are some ideas that hopefully help.
I understand that JobEndPointPK is implemented as an identifier component (see the Hibernate Reference, chapter 8.4). Note: it is critical that it implements the equals and hashCode` methods correctly, as Hibernate relies on these.
Updated: Provided that your JobEndPoint and JobEndPointPK looks something like this:
#Embeddable
class JobEndPointPK {
#Column(name = "type", nullable = false)
#Enumerated
EndPointType type;
#Column(name = "zipCode", nullable = false)
String zipCode;
#Column(name = "customerNumber", nullable = false)
int customerId;
// equals, hasCode, getters, setters etc.
}
#Entity
class JobEndPoint {
#Id
private JobEndPointPK key;
// getters, setters etc.
}
The mapping annotation would be something like:
#Entity
class Job {
#OneToOne
#JoinColumns ({
#JoinColumn(name="loadPointType", referencedColumnName = "type"),
#JoinColumn(name="loadPointZip", referencedColumnName = "zipCode"),
#JoinColumn(name="loadPointCust", referencedColumnName = "customerNumber")
})
private JobEndPoint loadPoint;
// similarly for unloadPoint
// other properties
}
The example is adapted from here.
I am not sure how to deal with JobEndPointPK.type though, as for loadPoint it is obviously Load and for unloadPoint, Unload, so you most probably don't want to store it separately in the DB. My gues is that you can specify the value with the #Formula annotation, but I haven't seen any concrete example for this.
Note that all this code is purely experimental, I haven't tested it.
There are other variations on the theme. For more details, see the section "Composite keys with annotations" in Chapter 8 of Java Persistence with Hibernate.

Categories