프로젝트/무인 사물함

[AIoT] 무인 사물함 프로젝트 MVC 모델 작성 Repository

마달랭 2025. 2. 11. 22:45
반응형

개요

MVC 아키텍처에서 Repository는 데이터 접근 및 영속성 로직을 캡슐화하는 중요한 구성 요소이다.

데이터베이스는 데이터베이스나 다른 저장소와 직접적인 상호작용을 담당한다.

주로 CRUD작업을 수행하며 비즈니스 로직과 데이터 액세스 로직을 분리하여 관심사의 분리를 실현한다.

 

데이터 접근 메서드들을 인터페이스로 정의하여 일관된 데이터 조작 방식을 제공한다.

모델 객체의 상태를 데이터베이스에 반영하거나, 데이터베이스에서 모델 객체로 데이터를 변환한다.

 

SpringBoot에서는 실제 쿼리를 통한 로직 구현 뿐만 아니라 Create와 Update를 진행해 주는 save와 findById를 통해 특정 레코드를 탐색하는 작업을 별도의 기능 작성 없이 사용할 수 있다.

 

하기에 레포지토리 인터페이스를 오름차순에 따라 정의한다.

 

 

AccessTokenRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.entity.AccessToken;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AccessTokenRepository extends JpaRepository<AccessToken, Long> {

}


AccessToken 테이블 관련 레포지토리 내부에 별다른 로직이 없다.

이유는 tokenId를 기준으로 조회하여 데이터를 DB에서 가져오고, tokenValue를 확인하는 간단한 로직밖에 없기 때문

해당 내용 정도의 로직은 JPA를 통해 별도 로직 작성 없이 바로 사용할 수 있다.

 

 

CertificationRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.entity.Certification;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface CertificationRepository extends JpaRepository<Certification, Long> {
    Optional<Certification> findByAdminIdAndAdminPassword(String adminId, String adminPassword);
}


관리자 로그인 시 AdminId와 AdminPassword를 통해 관리자 정보를 식별하기 위한 로직이 있다.

 

 

LockerLocationRepository.java

package com.a207.smartlocker.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.a207.smartlocker.model.entity.LockerLocation;

public interface LockerLocationRepository extends JpaRepository<LockerLocation, Long> {

}

 

사물함의 섹터 데이터를 갖고 있는 LockerLocation테이블 관련 레포지토리이다.

조회 작업 시 섹터 이름 정보를 파라미터로 전달받으므로 마찬가지로 별도 로직 작성 없이 JPA만으로 구현 가능

 

 

LockerQueueRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.entity.Locker;
import com.a207.smartlocker.model.entity.LockerQueue;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;

public interface LockerQueueRepository extends JpaRepository<LockerQueue, Long> {
    Optional<LockerQueue> findFirstByOrderByQueueIdAsc();
    @Query("SELECT q FROM LockerQueue q WHERE q.requestType = 'Retrieve' ORDER BY q.queueId ASC LIMIT 20")
    List<LockerQueue> findFirst20QRetrieveQueues();

    Optional<LockerQueue> findByLockerIdAndRequestType(Locker locker, String requestType);
}

 

작업 대기열 관련 테이블의 데이터를 가져오기 위한 레포지토리이다.

  • QueueId 기준 오름차순으로 정렬하여 데이터를 가져오는 로직
  • 반환 대기 작업 중 상위 20개의 작업의 데이터를 가져오는 로직
  • requestType별 작업의 데이터를 가져오는 로직

위 세가지 작업에 대한 로직이 존재한다. 이 중 20개의 작업을 가져온다는 로직에만 SQL을 통해 상세히 작성해 주었다.

 

 

LockerRepository.java

package com.a207.smartlocker.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.a207.smartlocker.model.entity.Locker;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface LockerRepository extends JpaRepository<Locker, Long> {
    Optional<Locker> findByLockerId(Long lockerId);

    @Query("SELECT l FROM Locker l WHERE l.lockerLocation.locationId IN " +
            "(SELECT loc.locationId FROM LockerLocation loc WHERE loc.locationName = :locationName)" +
    "ORDER BY l.lockerId")
    List<Locker> findLockersByLocationName(@Param("locationName") String locationName);
}


사물함 관련 레포지토리이다, 사물함 번호를 통해 데이터를 뽑아내는 간단한 로직이 있는 반면

서브쿼리를 사용하여 더욱 복잡한 데이터를 뽑아내는 로직이 존재한다.

 

 

LockerStatusRepository.java

package com.a207.smartlocker.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.a207.smartlocker.model.entity.LockerStatus;

public interface LockerStatusRepository extends JpaRepository<LockerStatus, Long> {

}


마찬가지로 해당 테이블은 사물함의 상태를 나타내기 위한 간단한 자료만 존재하므로 특별한 로직이 없다.

 

 

LockerUsageLogRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.dto.LockerUsageLogResponse;
import com.a207.smartlocker.model.dto.UserUsageResponse;
import org.springframework.data.jpa.repository.JpaRepository;
import com.a207.smartlocker.model.entity.LockerUsageLog;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

public interface LockerUsageLogRepository extends JpaRepository<LockerUsageLog, Long> {
    Optional<LockerUsageLog> findFirstByLocker_LockerIdAndStoreRobotIdIsNull(Long lockerId);
    Optional<LockerUsageLog> findFirstByLocker_LockerIdAndRetrieveRobotIdIsNull(Long lockerId);

    @Query("SELECT new com.a207.smartlocker.model.dto.UserUsageResponse(l.user.userId, l.user.phoneNumber, COUNT(l)) " +
            "FROM LockerUsageLog l " +
            "GROUP BY l.user.userId, l.user.phoneNumber " +
            "ORDER BY 1")
    List<UserUsageResponse> findUserUsageStatistics();

    @Query("SELECT l " +
            "FROM LockerUsageLog l " +
            "WHERE l.storeTime BETWEEN :startDate AND :endDate " +
            "ORDER BY l.logId ")
    List<LockerUsageLog> findByStoreTimeBetween(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate);
}


이용 로그를 기록하기 위한 사물함 번호가 일치하면서 수령/반환 작업을 담당한 로봇이 NULL인 한가지 행을 뽑는 로직이 존재한다.

또한 관리자 페이지를 위한 유저별 이용 횟수, 보관 시점을 기준으로 데이터를 가져오는 로직이 포함되어 있다.

 

 

RobotRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.dto.RobotResponse;
import com.a207.smartlocker.model.entity.Robot;
import com.a207.smartlocker.model.entity.RobotStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface RobotRepository extends JpaRepository<Robot, Long> {
    @Query(value = "UPDATE robots r SET robot_status_id = :newStatusId " +
            "WHERE r.robot_id IN (SELECT r2.robot_id FROM robots r2 WHERE r2.robot_status_id = :currentStatusId LIMIT 1) " +
            "RETURNING *",
            nativeQuery = true)
    Optional<Robot> findAndUpdateRobotStatus(@Param("currentStatusId") Long currentStatusId,
                                             @Param("newStatusId") Long newStatusId);

    @Modifying
    @Query("UPDATE Robot r SET r.robotStatus.robotStatusId = :newStatusId WHERE r.robotId = :robotId")
    void updateRobotStatus(@Param("robotId") Long robotId, @Param("newStatusId") Long newStatusId);

    @Query("SELECT r FROM Robot r ORDER BY r.robotId ASC")
    List<Robot> findAllByOrderByRobotIdAsc();
}

 

로봇의 작업 수행 혹은 완료 시 로봇의 상태를 변경하는 로직이 포함되어 있다.

여기에 PostgreSQL의 장점으로 RETURNING절이 있는데, UPDATE작업 후 작업의 결과를 리턴받을 수 있는 로직이다.

이 외에도 관리자 페이지용 로봇 정보를 Id를 기준으로 오름차순 정렬하여 데이터를 가져오는 로직이 있다.

 

 

RobotStatusRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.entity.RobotStatus;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RobotStatusRepository extends JpaRepository<RobotStatus, Long> {

}

 

LockerStatusRepository와 마찬가지로 해당 테이블은 로봇의 상태를 나타내기 위한 간단한 자료만 존재하므로 특별한 로직이 없다.

 

 

UserRepository.java

package com.a207.smartlocker.repository;

import com.a207.smartlocker.model.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByPhoneNumber(String phoneNumber);
}

 

유저의 핸드폰 번호를 통해 유저 관련 데이터를 가져오는 로직이 포함되어 있다.

마찬가지로 JPA로 인해 별도의 쿼리문은 필요로 하지 않는다.

728x90
반응형