Hibernate/JPA Query Optimierung

Hi,
ich stehe gerade vor einem Problem in meinem Projekt. Über JPA wird ein Query erstellt und abgeschickt was irrsinnig lange dauert (>20s), wenn ich genau das Query nehme was intern gebaut wird und kleine Optimierungen mache bin ich bei 0.1s.
Wie kann ich Einfluss nehmen darauf was für ein Query aufgebaut wird?

[SQL]public interface MessageParamRepository extends JpaRepository<AbstractMessageParam, Long> {

List<AbstractMessageParam> findByMessage(Message message);

}
[/SQL]
Darüber wird aktuell ein Query automatisch generiert, was am Ende so aussieht
[SQL]select abstractme0_.id as id1_0_, abstractme0_.custom_fields as custom_f2_0_, abstractme0_.message_id as message_4_0_, abstractme0_.param_key as param_ke3_0_, abstractme0_.badge_number as badge_nu1_11_, abstractme0_.content_available as content_2_11_, abstractme0_.message_expiry_time as message_3_11_, abstractme0_.sound_file as sound_fi4_11_, abstractme0_.notification_type as notifica1_12_, abstractme0_.tile_back_background_image as tile_bac2_12_, abstractme0_.tile_back_content as tile_bac3_12_, abstractme0_.tile_back_title as tile_bac4_12_, abstractme0_.tile_background_image as tile_bac5_12_, abstractme0_.tile_count as tile_cou6_12_, abstractme0_.toast_parameter as toast_pa7_12_, abstractme0_.toast_subtitle as toast_su8_12_, abstractme0_.clazz_ as clazz_ from ( select id, custom_fields, param_key, message_id, null as badge_number, null as content_available, null as message_expiry_time, null as sound_file, null as notification_type, null as tile_back_background_image, null as tile_back_content, null as tile_back_title, null as tile_background_image, null as tile_count, null as toast_parameter, null as toast_subtitle, 1 as clazz_ from message_params_aos union select id, custom_fields, param_key, message_id, badge_number, content_available, message_expiry_time, sound_file, null as notification_type, null as tile_back_background_image, null as tile_back_content, null as tile_back_title, null as tile_background_image, null as tile_count, null as toast_parameter, null as toast_subtitle, 2 as clazz_ from message_params_ios union select id, custom_fields, param_key, message_id, null as badge_number, null as content_available, null as message_expiry_time, null as sound_file, notification_type, tile_back_background_image, tile_back_content, tile_back_title, tile_background_image, tile_count, toast_parameter, toast_subtitle, 3 as clazz_ from message_params_wos ) abstractme0_ left outer join messages message1_ on abstractme0_.message_id=message1_.id where message1_.id=?[/SQL]

und das hier läuft um Welten schneller
[SQL]select abstractme0_.id as id1_0_, abstractme0_.custom_fields as custom_f2_0_, abstractme0_.message_id as message_4_0_, abstractme0_.param_key as param_ke3_0_, abstractme0_.badge_number as badge_nu1_11_, abstractme0_.message_expiry_time as message_2_11_, abstractme0_.sound_file as sound_fi3_11_, abstractme0_.notification_type as notifica1_12_, abstractme0_.tile_back_background_image as tile_bac2_12_, abstractme0_.tile_back_content as tile_bac3_12_, abstractme0_.tile_back_title as tile_bac4_12_, abstractme0_.tile_background_image as tile_bac5_12_, abstractme0_.tile_count as tile_cou6_12_, abstractme0_.toast_parameter as toast_pa7_12_, abstractme0_.toast_subtitle as toast_su8_12_, abstractme0_.clazz_ as clazz_ from (
select id, custom_fields, param_key, message_id, badge_number, message_expiry_time, sound_file, null as notification_type, null as tile_back_background_image, null as tile_back_content, null as tile_back_title, null as tile_background_image, null as tile_count, null as toast_parameter, null as toast_subtitle, 1 as clazz_ from message_params_ios iosp WHERE iosp.message_id =253843 union
select id, custom_fields, param_key, message_id, null as badge_number, null as message_expiry_time, null as sound_file, null as notification_type, null as tile_back_background_image, null as tile_back_content, null as tile_back_title, null as tile_background_image, null as tile_count, null as toast_parameter, null as toast_subtitle, 2 as clazz_ from message_params_aos aosp WHERE aosp.message_id = 253843 union
select id, custom_fields, param_key, message_id, null as badge_number, null as message_expiry_time, null as sound_file, notification_type, tile_back_background_image, tile_back_content, tile_back_title, tile_background_image, tile_count, toast_parameter, toast_subtitle, 3 as clazz_ from message_params_wos wp WHERE wp.message_id =253843
) abstractme0_ left outer join messages message1_ on abstractme0_.message_id=message1_.id where message1_.id=253843;[/SQL]
ich habe halt bei den Sub Selects gleich noch die Mengen reduziert.

Wie bekomme ich das in JPA/Hibernate rein?

Wie wird denn die jetzige Abfrage aufgebaut, HQL, JPQL?
Eine Native Query koente das Problem zwar loesen, ist aber nicht portabel und eher der letzte Ausweg.

Ich habe es direkt gar nicht gesehen, den letzten Part den ich sehe ist das Interface da oben

JpaRepository ist eine Spring-Erweiterung von JPA, oder?

Die Alternative wäre wohl eine eigene Query mittels Criteria oder JPQL.

Annotation oder HBM Dateien als Mapping?

Irgendwo muss das Mapping festgelegt werden, falls noch keine HQL/JPQL Query da ist, solltest du eine erstellen, so kannst du einiges beinflussen.

Ja ich glaube, ich gab leider das “fertige” Projekt vorgesetzt bekommen und muss jetzt ausbessern
Wie könnte das mit den anderen beiden aussehen?
Sitze gerade in Meetings daher kann ich nicht viel suchen

Wie gesagt, schau dich mal um wo das Mapping definiert ist, Annotationen oder XML, JPA oder Hibernate, dann koennen wir besser helfen :slight_smile:

Also das reine mapping ist mit annotations in den objekten
Aber direkt eine Definition eines Queries für das hab ich nicht gesehen

[quote=EagleEye]
Also das reine mapping ist mit annotations in den objekten
Aber direkt eine Definition eines Queries für das hab ich nicht gesehen[/quote]
Die Queries koennten in HBM mapping files stehen, oder in der orm.xml, oder eben in den Entities selber wenn Annotationen verwendet werden.

Wenn keine definiert sind, solltest du das machen, koennte das Problem schon loesen.

Oh hatte deine Sache oben gar nicht gesehen maki
Wie gesagt das mapping ist komplett per Annotation

Ja ich würde da gern nen query angeben, nur leider hab ich noch nicht gesehen wie ich da bisschen genauer angeben kann was ich will

Ne das ist alles in Annotations ohne große config files, daher habe ich schon einige Tage mit suchen verbracht was wo ist :smiley:

Genau genommen handelt es sich anscheinend um die Nutzung von SpringDATA. Wie man hier Custom Queries einhängen kann findet sich hier: http://docs.spring.io/spring-data/jpa/docs/1.6.1.RELEASE/reference/html/jpa.repositories.html#jpa.query-methods z.B. in dem man ein JPA Named Query an der Entity definiert.

[SQL]
left outer join messages message1_ on abstractme0_.message_id=message1_.id
[/SQL]
Sind das die beiden Primärschlüssel? Wenn nicht, Index in der Datenbank vorhanden?

Irgendwo ist da doch der Wurm drin, es gibt die drei Tabellen messageparams_xxx und messages, und wenn man alle params für eine message sucht, dann ist das eben die union - warum der outer join? Müsste man echt mal das Modell dazu anschauen…

Bleiglanz, sowie ich das sehe passt die Struktur schon, aber das Problem ist die Union
Da wird bei dem kleinen Dump den ich hab 3 Tabellen mit ~5, 8k und 15k nen Union gemacht und das kostet einfach Zeit. Zumal danach nur eine Zeile gebraucht wird.

Ah super mvitz, der Link sollte mir weiterhelfen danke

Aber warum der join? Du hast eine Message, dazu gibt es drei Parametertabellen - dass die 3 Teilabfragen nicht verkleinert liegt doch daran, dass er mit sich selbst joint und erst dann auf die MessageID reduziert - würde mich wundern, wenn Hibernate das als Strategie implementiert hätte?

So ein union - mit so wenig Zeilen (<30000) kostet doch heutzutage nicht 20 Sekunden …

Ka wasda ablauft ich weiß nur das es langsam ist (kann auch sein dass ich mich mit der zahl vertan hab)

Ja dass die union so spät reduziert wird ist das Problem

Die dementsprechenden Entities wären interessant

ich hatte noch keine Zeit weiter zu machen an dem Problem, ich musste erst einmal in nem anderen Projekt weiter arbeiten und noch die Tests zum laufen bekommen.
Morgen werde ich mich dann wieder dran setzen und dann gibts weitere Infos

Subqueries kann Hibernate in der from clause aber nicht (zumindest nicht mit HQL). Mit JPQL wirst du da also wahrscheinlich auch nicht weiterkommen:
http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch16.html#queryhql-subqueries:

Note that HQL subqueries can occur only in the select or where clauses.

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractMessageParam implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "message_id", nullable = false)
    private Message message;

    @Column(name = "custom_fields")
    private String customFields;

    @Column(name = "param_key")
    private String paramKey;}

@Entity
@Table(name = "message_params_aos")
public class AOSMessageParam extends AbstractMessageParam {

    /**
     *
     */
    private static final long serialVersionUID = 6186241298813341549L;

}
@Entity
@Table(name = "message_params_ios")
public class IOSMessageParam extends AbstractMessageParam {

    /**
     *
     */
    private static final long serialVersionUID = 5608287224149680548L;

    @Column(name = "badge_number")
    private Integer badgeNumber;

    @Column(name = "sound_file")
    private String soundFile;

    @Column(name = "message_expiry_time")
    private Integer messageExpiryTime;
    
    @Column(name ="content_available")
    private String contentAvailable;}
@Entity
@Table(name = "message_params_wos")
public class WOSMessageParam extends AbstractMessageParam {

    /**
     *
     */
    private static final long serialVersionUID = 7492990529064071699L;

    /**
     *
     */
    public WOSMessageParam() {
    }

    private WindowsNotificationEnum notificationType;

    private String tileBackgroundImage;
    private Integer tileCount;
    private String tileBackBackgroundImage;
    private String tileBackTitle;
    private String tileBackContent;

    private String toastSubtitle;
    private String toastParameter;}

Das sind die Objekte die in der Datenbank liegen und abgefragt werden