Migrate to MyBatis with proper Controller→Service→Mapper layering

- Add MyBatis Spring Boot Starter with XML mappers and domain classes
- Create 9 mapper interfaces + XML: Restaurant, Video, Channel, Review,
  User, Stats, DaemonConfig, Search, Vector
- Create 10 domain classes with Lombok: Restaurant, VideoSummary,
  VideoDetail, VideoRestaurantLink, Channel, Review, UserInfo,
  DaemonConfig, SiteVisitStats, VectorSearchResult
- Create 7 new service classes: RestaurantService, VideoService,
  ChannelService, ReviewService, UserService, StatsService,
  DaemonConfigService
- Refactor all controllers to be thin (HTTP + auth only), delegating
  business logic to services
- Refactor SearchService, PipelineService, DaemonScheduler, AuthService,
  YouTubeService to use mappers/services instead of JDBC/repositories
- Add Jackson SNAKE_CASE property naming for consistent API responses
- Add ClobTypeHandler for Oracle CLOB→String in MyBatis
- Add IdGenerator utility for centralized UUID generation
- Delete old repository/ package (6 files), JdbcConfig, LowerCaseKeyAdvice
- VectorService retains JDBC for Oracle VECTOR type support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-09 21:13:44 +09:00
parent 91d0ad4598
commit c16add08c3
63 changed files with 2155 additions and 1483 deletions

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tasteby.mapper.SearchMapper">
<resultMap id="restaurantMap" type="Restaurant">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<result property="region" column="region"/>
<result property="latitude" column="latitude"/>
<result property="longitude" column="longitude"/>
<result property="cuisineType" column="cuisine_type"/>
<result property="priceRange" column="price_range"/>
<result property="googlePlaceId" column="google_place_id"/>
<result property="businessStatus" column="business_status"/>
<result property="rating" column="rating"/>
<result property="ratingCount" column="rating_count"/>
</resultMap>
<select id="keywordSearch" resultMap="restaurantMap">
SELECT DISTINCT r.id, r.name, r.address, r.region, r.latitude, r.longitude,
r.cuisine_type, r.price_range, r.google_place_id,
r.business_status, r.rating, r.rating_count
FROM restaurants r
JOIN video_restaurants vr ON vr.restaurant_id = r.id
JOIN videos v ON v.id = vr.video_id
WHERE r.latitude IS NOT NULL
AND (UPPER(r.name) LIKE UPPER(#{query})
OR UPPER(r.address) LIKE UPPER(#{query})
OR UPPER(r.region) LIKE UPPER(#{query})
OR UPPER(r.cuisine_type) LIKE UPPER(#{query})
OR UPPER(vr.foods_mentioned) LIKE UPPER(#{query})
OR UPPER(v.title) LIKE UPPER(#{query}))
FETCH FIRST #{limit} ROWS ONLY
</select>
<select id="findChannelsByRestaurantIds" resultType="map">
SELECT DISTINCT vr.restaurant_id, c.channel_name
FROM video_restaurants vr
JOIN videos v ON v.id = vr.video_id
JOIN channels c ON c.id = v.channel_id
WHERE vr.restaurant_id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>