前回までのあらすじ&今回の目標
カレンダー作成②では、表示させるメニューを「JavaScriptに直書きしたデータ」から「サーバ上で用意したデータ」に置き換えることを行いました。
そして今回は「データベースに登録されているデータ」に置き換えていきます!
なお、データベースは「MySQL」を使用しますが、インストール等の事前準備は今回省いています。
開発環境
・Java:17.0.12
・Spring Boot:3.4.1
・MySQL:8.0.37
データベースの準備
用意するテーブルは以下のようにしました。
テーブル名 | 内容 |
---|---|
menu | メニューを登録するテーブル |
dishes | 料理名をもつテーブル |
meal_types | 食事の分類とそれに対応する色をもつテーブル |
ER図は下記の通りです。menuテーブルに日々のメニューを登録していき、料理名や食事の分類(朝食、昼食、夕食など)は別のテーブルで管理して、取得時に結合するようにしています。
○ER図
create table dishes (
dish_code CHAR(7) not null
, dish_name VARCHAR(90) not null
, primary key (dish_code)
)
create table meal_types (
meal_type_code CHAR(1) not null
, meal_type_name VARCHAR(30) not null
, color VARCHAR(12) not null
, primary key (meal_type_code)
)
create table menu (
menu_id BIGINT not null auto_increment
, eating_date DATE not null
, meal_type_code CHAR(1) not null
, dish_code CHAR(7) not null
, primary key (menu_id)
, constraint fk_dish_code foreign key (dish_code) references dishes(dish_code)
, constraint fk_meal_type_code foreign key (meal_type_code) references meal_types(meal_type_code)
)
ソース等の修正
build.gradleの修正
次にMySQLのデータにアクセスするのに必要なライブラリを追加するため、「build.gradle」の修正を行います。
以下のように「spring-boot-starter-data-jdbc」と「mysql-connector-j」を追加しました。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' // 追加
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j' // 追加
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
application.propertiesの修正
build.gradleの修正が完了したら「application.properrties」の修正を行います。ここではデータベースにアクセスする際の各情報を記載しました。
# データベースのURL(db_foodappスキーマに接続する)
spring.datasource.url=jdbc:mysql://localhost:3306/db_foodapp
# ユーザ名
spring.datasource.username=root
# パスワード
spring.datasource.password=*******
# ドライバ
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 文字コード
spring.sql.init.encoding=utf-8
データ取得部分の修正
上記が完了したら、いよいよデータ取得部分の修正を行っていきます。
まずControllerクラスですが、「/foodapp/calendar」にアクセスされた場合に動作する関数を下記のように変更しました。
変更箇所は複数ありますが、データ取得処理をServiceクラスで行っているところ(15行目)が一番大きな変更点になります。
@Controller
@RequestMapping("/foodapp")
public class FoodAppController {
@Autowired
CalendarManagementService calendarManagementService;
@GetMapping("calendar")
public String showCalendar(HttpSession session, Model model) {
Date date = new Date(session.getCreationTime());
String year = new SimpleDateFormat("yyyy").format(date);
String month = new SimpleDateFormat("MM").format(date);
List<EventBean> events = calendarManagementService.getEventsOfMonth(year, month);
model.addAttribute("events", events);
return "calendar";
}
}
そのServiceクラスの実装クラスは、Repositoryクラスを介してデータ取得を行うような設計としました。
@Service
class CalendarManagementServiceImpl implements CalendarManagementService {
@Autowired
CalendarRepository calendarRepository;
@Override
public List<EventBean> getEventsOfMonth(String year, String month) {
return calendarRepository.getEvents(year, month);
}
}
Repositoryクラスの実装クラスでは、渡された「年・月」の文字列から対象月のデータを複数取得し、それぞれをEventBeanインスタンスに格納して戻す処理を実装しています。SqlConstクラスはxmlファイルに記載したSQLの文字列を戻すクラスとなっています。
@Repository
public class CalendarRepositoryImpl implements CalendarRepository {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public List<EventBean> getEvents(String year, String month) {
String date = year + month + "01";
List<EventBean> events = jdbcTemplate.query(
SqlConst.getQueryFromXml(SqlConst.MENU_FOR_EACH_MONTH),
(rs, rowNum) -> new EventBean()
.start(rs.getString("start"))
.title(rs.getString("title"))
.backgroundColor(rs.getString("backgroundColor")),
date,
date);
return events;
}
}
public class SqlConst {
private static final String XML_FOLDER_PATH = System.getProperty("user.dir")
+ "\\src\\main\\resources\\sql\\";
public static final String MENU_FOR_EACH_MONTH = "menu.forEachMonth";
public static String getQueryFromXml(String queryId) {
String sql = null;
// 引数からテーブル名とIDを取得
String tableName = queryId.split("\\.")[0];
String id = queryId.split("\\.")[1];
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
// 引数よりxmlファイルを特定
Document doc = builder.parse(new File(XML_FOLDER_PATH + tableName + ".xml"));
NodeList sqlList = doc.getElementsByTagName("query");
// idが同じノードの値を取得する
for (int i = 0; i < sqlList.getLength(); i++) {
Node node = sqlList.item(i);
Node attr = node.getAttributes().getNamedItem("id");
if (attr.getNodeValue().equals(id)) {
sql = node.getTextContent();
break;
}
}
} catch (ParserConfigurationException | SAXException | IOException e) {
// 今回、例外処理は割愛
e.printStackTrace();
}
return Objects.nonNull(sql) ? sql.replaceAll("^\t", "") : null;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<sql table="menu">
<query id="forEachMonth" comment="各月のメニューを取得">
<![CDATA[
select
menu.eating_date start
, dishes.dish_name title
, menu.meal_type_code code
, meal_types.color backgroundColor
from
menu
inner join
dishes
on
menu.dish_code = dishes.dish_code
inner join
meal_types
on
menu.meal_type_code = meal_types.meal_type_code
where
menu.eating_date between str_to_date(?, '%Y%m%d') and last_day(str_to_date(?, '%Y%m%d'))
order by
start
, code
]]>
</query>
</sql>
最後にBeanクラスですが、こちらはRepositoryクラスのラムダ式で記述が簡略ができるように引数をフィールドにセットした後に自身の参照値を返す関数を追加しました。
またカレンダーに表示した際にメニューの境界線の色が見にくかったため、「borderColor」のプロパティ(10行目)も追記しています。
@Data
public class EventBean {
// 食べた日付
private String start;
// 食べた料理名
private String title;
// 朝食:red、昼食:orange、夕食:blue
private String backgroundColor;
// 画面に表示する際の境界線の色
private String borderColor = "white";
// startフィールドを更新して自身の参照値を返す
public EventBean start(String start) {
this.start = start;
return this;
}
// titleフィールドを更新して自身の参照値を返す
public EventBean title(String title) {
this.title = title;
return this;
}
// backgroundColorフィールドを更新して自身の参照値を返す
public EventBean backgroundColor(String backgroundColor) {
this.backgroundColor = backgroundColor;
return this;
}
}
動作確認
各テーブルには下記の通りデータを登録して、いざアクセスするとしっかりデータベースの内容が反映されていることが確認できました!
menu_id | eating_date | meal_type_code | dish_code |
---|---|---|---|
1 | 2025/02/01 | 1 | 0000001 |
2 | 2025/02/01 | 2 | 0000002 |
3 | 2025/02/01 | 3 | 0000003 |
4 | 2025/02/02 | 3 | 0000004 |
5 | 2025/02/03 | 1 | 0000005 |
dish_code | dish_name |
---|---|
0000001 | パン |
0000002 | ハンバーグ |
0000003 | 唐揚げ丼 |
0000004 | 恵方巻 |
0000005 | おにぎり |
meal_type_code | meal_type_name | color |
---|---|---|
1 | 朝食 | red |
2 | 昼食 | orange |
3 | 夕食 | blue |
○出力結果
これで今後はテーブルにデータを登録していくだけで画面に自動反映されるようになりました!
今後の予定
今回はデータベースに登録されているデータを取得して画面に反映させるところまで進めました。だんだんWebアプリっぽくなってきましたね!
次回はデータベースにデータを登録する画面の実装を行っていきます。
今日の夕飯は「餃子」でした。それではまた。。
コメント