顯示具有 Spring Data JPA 標籤的文章。 顯示所有文章
顯示具有 Spring Data JPA 標籤的文章。 顯示所有文章

2018年11月8日 星期四

org.hibernate.QueryException: could not resolve property: “xxx”

寫 Spring Data JPA 或者 Hibernate 時很重要的一點,就是在定義 Repository 時,對應的欄位要用 Entity 裡的變數名字,而不是實際資料庫裡的資料欄位名字。這也包括了當要使用 Pageable 等物件時,物件裡宣告的欄位也要用 Entity 的變數名字。

參考資料
  1. 数据库异常整理:org.hibernate.QueryException: could not resolve property: “xxx”
  2. SSH整合报错:org.hibernate.hql.internal.ast.QuerySyntaxException: User is not mapped[......]

2018年10月23日 星期二

在 Spring Data JPA 中使用 AUTO_INCREMENT

快速紀錄~使用 @Id 和 @GeneratedValue 就可以用自動遞增的 Primary Key 了。不過如果不想要讓 Hibernate 自己產生 ID,而是直接靠資料庫自行產生的話,可以設定 @GeneratedValue 的屬性 strategy = GenerationType.IDENTITY。

參考資料
  1. Spring Data JPA; save() auto increment primary key error
  2. JPA EclipseLink DatabaseException: 'table foo.SEQUENCE doesn't exist'

2018年5月21日 星期一

透過 Spring Data JPA 存取 DynamoDB

因為 Spring Data JPA 用來存取資料庫時很方便,然後 DynamoDB 又是屬於特別囉唆的東西,所以之前在研究 Spring Boot 時,就在想不知道 Spring 是否有支援 DynamoDB 的實作。很幸運地,社群真的有人做了 Spring Data JPA + DynamoDB [1-2] 造福大家!XD

2018年4月11日 星期三

Spring Data JPA 的 JOIN 操作

其實在 Spring 上要做 JOIN,還挺麻煩的…
如果可以的話,我覺得能不要做就不要做比較好 XD

不過真的需要的時候,如果是需要 SELECT 時做 JOIN,推薦可以參考 [1-2] 的說明。
實務應用的話,假設以 logback 輸出到 SQL Server 的格式作為範例
想要一次取得整個 log 包含相關的 property 和 stack trace,以下的 Entity 範例是能夠套用。

logback 輸出 SQL Server 的 schema

schema 可以參考 [3],以下為 schema 的內容:

CREATE TABLE logging_event 
  ( 
    timestmp          DECIMAL(20) NOT NULL,
   	formatted_message VARCHAR(4000) NOT NULL,
    logger_name       VARCHAR(254) NOT NULL,
    level_string      VARCHAR(254) NOT NULL,
    thread_name       VARCHAR(254),
    reference_flag    SMALLINT,
    arg0              VARCHAR(254),
    arg1              VARCHAR(254),
    arg2              VARCHAR(254),
    arg3              VARCHAR(254),
    caller_filename   VARCHAR(254) NOT NULL,
    caller_class      VARCHAR(254) NOT NULL,
    caller_method     VARCHAR(254) NOT NULL,
    caller_line       CHAR(4) NOT NULL,
    event_id          DECIMAL(40) NOT NULL identity,
    PRIMARY KEY(event_id) 
  ) 

CREATE TABLE logging_event_property 
  ( 
    event_id          DECIMAL(40) NOT NULL, 
    mapped_key        VARCHAR(254) NOT NULL, 
    mapped_value      VARCHAR(1024), 
    PRIMARY KEY(event_id, mapped_key), 
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id) 
  ) 

CREATE TABLE logging_event_exception 
  ( 
    event_id         DECIMAL(40) NOT NULL, 
    i                SMALLINT NOT NULL, 
    trace_line       VARCHAR(254) NOT NULL, 
    PRIMARY KEY(event_id, i), 
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id) 
  )
Entity

假設我的 Entity 物件名字是 Activity、ActivityProperty、ActivityStackTrace,分別定義如下:

Activity

@Entity(name = "logging_event")
public class Activity implements Serializable {
  @Id
  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "timestmp")
  private Long timestmp;
  
  @Column(name = "formatted_message")
  private String message;
  
  @Column(name = "logger_name")
  private String logger;
  
  @Column(name = "level_string")
  private String level;
  
  @Column(name = "thread_name")
  private String thread;
  
  @Column(name = "reference_flag")
  private Integer reference;
  
  @Column(name = "arg0")
  private String arg0;
  
  @Column(name = "arg1")
  private String arg1;
  
  @Column(name = "arg2")
  private String arg2;
  
  @Column(name = "arg3")
  private String arg3;
  
  @Column(name = "caller_filename")
  private String callerFile;
  
  @Column(name = "caller_class")
  private String callerClass;
  
  @Column(name = "caller_method")
  private String callerMethod;
  
  @Column(name = "caller_line")
  private String callerLine;
  
  @OneToMany(
      mappedBy = "eventId",
      cascade = CascadeType.ALL,
      fetch = FetchType.EAGER)
  private List<ActivityProperty> properties;
  
  @OneToMany(cascade = CascadeType.ALL)
  @JoinColumn(name = "event_id", referencedColumnName = "event_id")
  @LazyCollection(LazyCollectionOption.FALSE)
  private List<ActivityStackTrace> stackTraces;
}

ActivityProperty

@Entity(name = "logging_event_property")
public class ActivityProperty implements Serializable {
  private static final long serialVersionUID = -6532428503805543364L;

  @Id
  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "mapped_key")
  private String key;
  
  @Column(name = "mapped_value")
  private String value;
}

ActivityStackTrace

@Entity(name = "logging_event_exception")
public class ActivityStackTrace implements Serializable {
  private static final long serialVersionUID = -50856985039937262L;

  @EmbeddedId
  private ActivityStackTraceId id;
  
  @Column(name = "trace_line")
  private String line;
}

ActivityStackTraceId

@Embeddable
public class ActivityStackTraceId implements Serializable {
  private static final long serialVersionUID = -4496556483758827756L;

  @Column(name = "event_id")
  private Long eventId;
  
  @Column(name = "i")
  private Integer i;
}

其中在 Activity 要連結 ActivityProperty 和 ActivityStackTrace 時,都是以 @OneToMany 來標注
用來表示一個 Activity 可以有多個 ActivityProperty 和 ActivityStackTrace。
此外,由於 ActivityStackTrace 並非是單一 ID,而是 Composite Primary Key 的形式
因此要利用 @Embeddable來組合。這部份可以參考 [4]。
同時,使用了 @Embeddable 之後,如果 JOIN 操作依然使用 mappedBy 的話,會出現 [5] 提到的錯誤
目前其實還沒有搞很懂這個錯誤是什麼意思,不過姑且使用 [5] 的作法,改成用 LazyCollection 可以解決。
未來如果有把問題搞懂的話,再回來追加補充了 @@a

除此之外,還得稍微注意一下,上述的 JOIN 寫法,有可能導致 [6] 描述的問題
也就是當要做資料刪除時,Hibernate 產生的 SQL Statement 會用很沒效率的方式在刪除關聯
而且某些狀況下有可能會刪除沒有預期要刪除的資料。

參考資料
  1. JPA,在@OneToMany里加入mappedBy属性
  2. hibernate基于注解的维护权反转:@OneToMany(mappedBy=)
  3. mssql.sql
  4. The best way to map a Composite Primary Key with JPA and Hibernate
  5. Hibernate cannot simultaneously fetch multiple bags
  6. Why you should avoid CascadeType.REMOVE for to-many associations and what to do instead
  7. @JoinColumn 详解

QuerySyntaxException: Path expected for join

參考資料
  1. HQL ERROR: Path expected for join