質問編集履歴
2
単体のエンティティからの検索はできましたが、コンストラクタを通すと失敗します。
title
CHANGED
File without changes
|
body
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
### 前提・実現したいこと
|
2
2
|
|
3
3
|
プログラミングを始めて三か月半の初心者です。
|
4
|
-
JPQLで複数のデータベースを結合して検索し、結果をビューに一覧表示したいところで詰まっています。
|
5
|
-
下記の三つのテーブルを使用します。{}がテーブル名、以下","で区切られているのがカラム名です。
|
6
4
|
|
5
|
+
Hibernateを使って複数のエンティティからデータを検索する方法を知りたいです。
|
6
|
+
|
7
|
+
前提として、下記の三つのテーブルを使用します。{}がテーブル名、以下","で区切られているのがカラム名です。
|
8
|
+
|
7
9
|
- {Highlight}
|
8
10
|
id, video_id, highlight, comment, user_id, created_at, updated_at, public_flag
|
9
11
|
- {Video}
|
@@ -14,7 +16,7 @@
|
|
14
16
|
Highlightテーブルのvideo_idをVideoテーブルにあるidに、user_idをUserテーブルにあるidにそれぞれ結合させます。
|
15
17
|
VideoとHighlight、UserとHighlightがそれぞれ一対多の関係性です。
|
16
18
|
|
17
|
-
|
19
|
+
下記はJPQL文の一例です。
|
18
20
|
```
|
19
21
|
SELECT h.id, h.highlight, h.updated_at, h.public_flag, v.title, u.name, u.delete_flag FROM Highlight h JOIN Video v ON video_id = v.id JOIN User u ON user_id = u.id ORDER BY h.id DESC
|
20
22
|
```
|
@@ -38,62 +40,34 @@
|
|
38
40
|
### 該当のソースコード
|
39
41
|
一覧の並び順を変えるため、JPQL文の内容を分岐させるクラスを作成しています。
|
40
42
|
```
|
41
|
-
package action.
|
43
|
+
package action.videos;
|
42
44
|
|
43
|
-
public class
|
45
|
+
public class SearchVideo {
|
44
46
|
public static String IndexSortMethod(Integer n) {
|
45
|
-
String[] isList = new String[
|
47
|
+
String[] isList = new String[4];
|
46
48
|
|
47
|
-
isList[0] = "
|
49
|
+
isList[0] = "v.id DESC";
|
48
|
-
isList[1] = "
|
50
|
+
isList[1] = "v.id ASC";
|
49
|
-
isList[2] = "
|
51
|
+
isList[2] = "v.title DESC";
|
50
|
-
isList[3] = "
|
52
|
+
isList[3] = "v.title ASC";
|
51
|
-
isList[4] = "h.access DESC";
|
52
|
-
isList[5] = "h.access ASC";
|
53
53
|
|
54
|
-
StringBuilder is = new StringBuilder("SELECT
|
54
|
+
StringBuilder is = new StringBuilder("SELECT v.id, v.youtube_id, v.title, v.publishedAt, v.thumbnail FROM Video v ORDER BY ");
|
55
55
|
is.append(isList[n]);
|
56
56
|
String indexSort = is.toString();
|
57
57
|
|
58
|
+
// String indexSort = "SELECT v FROM Video AS v ORDER BY v.id DESC";
|
59
|
+
|
58
60
|
return indexSort;
|
59
61
|
}
|
60
62
|
}
|
63
|
+
|
61
64
|
```
|
62
65
|
一覧表示させるためのサーブレット
|
63
66
|
```
|
64
|
-
package controllers.highlights;
|
65
67
|
|
66
|
-
import java.io.IOException;
|
67
|
-
|
68
|
+
// ...
|
68
69
|
|
69
|
-
import javax.persistence.EntityManager;
|
70
|
-
import javax.servlet.RequestDispatcher;
|
71
|
-
import javax.servlet.ServletException;
|
72
|
-
import javax.servlet.annotation.WebServlet;
|
73
|
-
import javax.servlet.http.HttpServlet;
|
74
|
-
import javax.servlet.http.HttpServletRequest;
|
75
|
-
import javax.servlet.http.HttpServletResponse;
|
76
|
-
|
77
|
-
import action.highlights.SearchHighlight;
|
78
|
-
import models.highlights.IndexHighlight;
|
79
|
-
import utils.DBUtil;
|
80
|
-
|
81
|
-
/**
|
82
|
-
* Servlet implementation class HighlightsIndexServlet
|
83
|
-
*/
|
84
|
-
@WebServlet("/highlights/index")
|
85
|
-
public class HighlightsIndexServlet extends HttpServlet {
|
86
|
-
private static final long serialVersionUID = 1L;
|
87
|
-
|
88
70
|
/**
|
89
|
-
* @see HttpServlet#HttpServlet()
|
90
|
-
*/
|
91
|
-
public HighlightsIndexServlet() {
|
92
|
-
super();
|
93
|
-
// TODO Auto-generated constructor stub
|
94
|
-
}
|
95
|
-
|
96
|
-
/**
|
97
71
|
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
|
98
72
|
*/
|
99
73
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
@@ -118,86 +92,124 @@
|
|
118
92
|
if(request.getParameter("indexSort") != null && !request.getParameter("indexSort").equals("")) {
|
119
93
|
sortNumber = Integer.parseInt(request.getParameter("indexSort"));
|
120
94
|
}
|
121
|
-
String sort =
|
95
|
+
String sort = SearchVideo.IndexSortMethod(sortNumber);
|
122
|
-
List<
|
96
|
+
List<Video> videos = em.createQuery(sort, Video.class)
|
123
97
|
.setFirstResult(lines_per_page * (page - 1))
|
124
98
|
.setMaxResults(lines_per_page)
|
125
99
|
.getResultList();
|
126
100
|
|
101
|
+
long videos_count = (long)em.createNamedQuery("getVideosCount", Long.class)
|
102
|
+
.getSingleResult();
|
103
|
+
|
104
|
+
em.close();
|
105
|
+
|
127
106
|
// ...
|
107
|
+
|
128
108
|
}
|
129
109
|
```
|
130
|
-
検索したデータをそれぞれの変数に格納するコンストラクタ
|
110
|
+
検索したデータをそれぞれの変数に格納するコンストラクタ(Video)
|
131
111
|
```
|
132
|
-
package models.highlights;
|
133
112
|
|
113
|
+
// ...
|
114
|
+
|
115
|
+
package models.videos;
|
116
|
+
|
117
|
+
import java.sql.Blob;
|
134
118
|
import java.sql.Timestamp;
|
135
119
|
|
136
|
-
public class
|
120
|
+
public class IndexVideo {
|
137
121
|
private Integer id;
|
138
|
-
private String
|
122
|
+
private String youtube_id;
|
139
|
-
private Timestamp updated_at;
|
140
|
-
private Integer public_flag;
|
141
123
|
private String title;
|
124
|
+
private Timestamp publishedAt;
|
142
|
-
private
|
125
|
+
private Blob thumbnail;
|
143
|
-
private Integer delete_flag;
|
144
126
|
|
145
|
-
public
|
127
|
+
public IndexVideo() {}
|
146
128
|
|
147
|
-
public
|
129
|
+
public IndexVideo(Integer id, String youtube_id, String title, Timestamp publishedAt, Blob thumbnail) {
|
148
130
|
this.id = id;
|
149
|
-
this.highlight = highlight;
|
150
|
-
this.
|
131
|
+
this.youtube_id = youtube_id;
|
151
|
-
this.public_flag = public_flag;
|
152
132
|
this.title = title;
|
133
|
+
this.publishedAt = publishedAt;
|
153
|
-
this.
|
134
|
+
this.thumbnail = thumbnail;
|
154
|
-
this.delete_flag = delete_flag;
|
155
135
|
}
|
156
136
|
|
157
137
|
// 以下、getter/setter
|
138
|
+
|
158
139
|
```
|
159
140
|
結果を一覧表示させるビュー
|
160
141
|
```
|
161
|
-
|
142
|
+
|
162
|
-
|
143
|
+
// ...
|
163
|
-
|
144
|
+
|
164
145
|
<c:import url="../layout/app.jsp">
|
165
146
|
<c:param name="content">
|
166
|
-
<c:if test="${flush != null}">
|
167
|
-
<div id="${flush_success}">
|
168
|
-
|
147
|
+
<h2><a href="<c:url value='/videos/index' />">けそポテトチャンネルの動画</a></h2>
|
169
|
-
</div>
|
170
|
-
</c:if>
|
171
148
|
|
172
|
-
<h2><a href="<c:url value='/highlights/index' />">名シーン一覧</a></h2>
|
173
|
-
|
174
|
-
<form method="GET" action="<c:url value='/
|
149
|
+
<form method="GET" action="<c:url value='/videos/index' />">
|
175
150
|
<select name="indexSort">
|
176
151
|
<option value="">並び替え</option>
|
177
152
|
<option value="0">投稿日△</option>
|
178
153
|
<option value="1">投稿日▽</option>
|
179
154
|
<option value="2">タイトル△</option>
|
180
155
|
<option value="3">タイトル▽</option>
|
181
|
-
<option value="4">アクセス数△</option>
|
182
|
-
<option value="5">アクセス数▽</option>
|
183
156
|
</select>
|
184
157
|
<button type="submit">決定</button>
|
185
158
|
</form>
|
186
159
|
|
187
160
|
// ...
|
161
|
+
|
188
162
|
```
|
189
|
-
|
163
|
+
VideoテーブルのDTO
|
190
164
|
```
|
165
|
+
|
191
|
-
|
166
|
+
// ...
|
167
|
+
|
192
168
|
@Entity
|
169
|
+
public class Video {
|
170
|
+
@Id
|
171
|
+
@Column(name = "id")
|
172
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
173
|
+
private Integer id;
|
174
|
+
|
175
|
+
@Column(name = "youtube_id", nullable = false, unique = true)
|
176
|
+
private String youtube_id;
|
177
|
+
|
178
|
+
@Column(name = "title", nullable = false)
|
179
|
+
private String title;
|
180
|
+
|
181
|
+
@Column(name = "publishedAt", nullable = false)
|
182
|
+
private Timestamp publishedAt;
|
183
|
+
|
184
|
+
@Column(name = "thumbnail", nullable = false)
|
185
|
+
private Blob thumbnail;
|
186
|
+
|
187
|
+
@Column(name = "tag")
|
188
|
+
private String tag;
|
189
|
+
|
190
|
+
@Column(name = "taisyo", nullable = false)
|
191
|
+
private Integer taisyo;
|
192
|
+
|
193
|
+
@OneToMany(mappedBy = "video")
|
194
|
+
private List<Highlight> highlights;
|
195
|
+
|
196
|
+
// 以下、getter/setter
|
197
|
+
|
198
|
+
```
|
199
|
+
HighlightテーブルのDTOクラス
|
200
|
+
```
|
201
|
+
|
202
|
+
// ...
|
203
|
+
|
204
|
+
@Entity
|
193
205
|
public class Highlight {
|
194
206
|
@Id
|
195
207
|
@Column(name = "id")
|
196
208
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
197
209
|
private Integer id;
|
198
210
|
|
199
|
-
@Column(name = "video_id", nullable = false)
|
211
|
+
// @Column(name = "video_id", nullable = false)
|
200
|
-
private Integer video_id;
|
212
|
+
// private Integer video_id;
|
201
213
|
|
202
214
|
@Column(name = "highlight", length = 255, nullable = false, unique = true)
|
203
215
|
private String highlight;
|
@@ -208,8 +220,8 @@
|
|
208
220
|
@Column(name = "highlight_time")
|
209
221
|
private Time highlight_time;
|
210
222
|
|
211
|
-
@Column(name = "user_id")
|
223
|
+
// @Column(name = "user_id")
|
212
|
-
private Integer user_id;
|
224
|
+
// private Integer user_id;
|
213
225
|
|
214
226
|
@Column(name = "created_at", nullable = false)
|
215
227
|
private Timestamp created_at;
|
@@ -228,9 +240,11 @@
|
|
228
240
|
|
229
241
|
// 以下、getter/setter
|
230
242
|
```
|
231
|
-
UserテーブルのDTO
|
243
|
+
UserテーブルのDTOクラス
|
232
244
|
```
|
245
|
+
|
233
|
-
|
246
|
+
// ...
|
247
|
+
|
234
248
|
@Entity
|
235
249
|
public class User {
|
236
250
|
@Id
|
@@ -257,47 +271,33 @@
|
|
257
271
|
private List<Highlight> highlights;
|
258
272
|
|
259
273
|
// 以下、getter/setter
|
274
|
+
|
260
275
|
```
|
261
|
-
VideoテーブルのDTO
|
262
|
-
```
|
263
|
-
@Table(name = "videos")
|
264
|
-
@Entity
|
265
|
-
public class Video {
|
266
|
-
@Id
|
267
|
-
@Column(name = "id")
|
268
|
-
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
269
|
-
private Integer id;
|
270
276
|
|
271
|
-
@Column(name = "youtube_id", nullable = false, unique = true)
|
272
|
-
|
277
|
+
### 試したこと
|
273
278
|
|
274
|
-
|
279
|
+
それぞれのテーブルのDTOにOneToMany、ManyToOneのアノテーションを記述しました。
|
275
|
-
private String title;
|
276
280
|
|
277
|
-
@Column(name = "publishedAt", nullable = false)
|
278
|
-
|
281
|
+
### 追記
|
279
282
|
|
280
|
-
|
283
|
+
別クラスに作成したコンストラクタを介さずに、単体のエンティティからデータを検索することはできました。しかし、コンストラクタを通すと単体のエンティティを検索してもエラーが表示されてしまいます。複数のエンティティの場合でも同様です。
|
281
|
-
|
284
|
+
つまり、コンストラクタが上手く機能していないようなのです。
|
285
|
+
```
|
286
|
+
HTTP ERROR 500
|
282
287
|
|
283
|
-
|
288
|
+
Problem accessing /videos/index. Reason:
|
284
|
-
|
289
|
+
Server Error
|
285
290
|
|
286
|
-
@Column(name = "taisyo", nullable = false)
|
287
|
-
private Integer taisyo;
|
288
291
|
|
292
|
+
Caused by:
|
289
|
-
|
293
|
+
java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return using requested result type [models.videos.IndexVideo]
|
290
|
-
private List<Highlight> highlights;
|
291
294
|
|
292
|
-
//
|
295
|
+
// ...
|
296
|
+
|
293
297
|
```
|
294
298
|
|
295
|
-
|
299
|
+
なお、OneToMany(VideoクラスとUserクラス)とManyToOne(Highlightクラス)のアノテーションをそれぞれDTOに記載するにあたって、Videoテーブルのidに対応するvideo_idカラムとUserテーブルのidに対応するuser_idを消しました(コメント状態にした部分です)。
|
296
300
|
|
297
|
-
別パッケージにコンストラクタを作り、MySQLから検索してくるデータにそれぞれ変数を宣言しました。
|
298
|
-
ビューから送られてきたidによってJPQL分の一部を分岐させられるようにしました(並び替え表示をするためです)。
|
299
|
-
それぞれのテーブルのDTOにOneToMany、ManyToOneのアノテーションを記述しました。
|
300
|
-
|
301
301
|
### 最後に
|
302
302
|
質問が大変長く、コードの内容に至らない点が散見されると思いますが、ご回答のほどお待ちしております。
|
303
303
|
|
1
title
CHANGED
File without changes
|
body
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
id, name, address, created_at, delete_flag
|
13
13
|
|
14
14
|
Highlightテーブルのvideo_idをVideoテーブルにあるidに、user_idをUserテーブルにあるidにそれぞれ結合させます。
|
15
|
+
VideoとHighlight、UserとHighlightがそれぞれ一対多の関係性です。
|
15
16
|
|
16
17
|
Hibernateを利用して、複数のデータを検索するJPQL文を書きました。その一例です。
|
17
18
|
```
|