티스토리 뷰
> Using Spring Data Repositories with Spring Boot
> Entity 생성, Reposiotry생성, JPA설정 등 기본 사항은 이전 정리글 참고
http://leeyongjin.tistory.com/entry/SpringBoot
> Abstracting Common Transactional Attributes into a Mapped Superclass
> Transactional Entity Attributes
- id - the primary key
- referenceId - an external identifier; something portable
- version - facilitates optimistic locking
- createdBy - the user that created the entity; insert
- createdAt - the creation timestamp
- updatedBy - the user that last modified the entity; update
- updatedAt - the last modification timestatmp
> transactional Entity Atrribuate에 해당하는 칼럼을 포함하는 테이블 구성
> transational entity attribute 를 가지는 공통 부모 Entity Class 생성
> entity class들은 위 TransactionalEntity 클래스를 상속받는다.
@Entity
public class Account extends TransactionalEntity{
private static final long serialVersionUID = 1L;
// id attribute는 TransactionalEntity 상속받으므로 생략
// @Id
// @GeneratedValue
// private Long id;
> 로그인 사용자 ID를 처리하기 위한 ThreadLocal을 이용한 RequestContext Util 클래스 작성
public class RequestContext {
private static ThreadLocal<String> usernames = new ThreadLocal<String>();
private RequestContext(){
}
public static String getUserName(){
return usernames.get();
}
public static void setuserName(String username){
usernames.set(username);
}
public static void init(){
usernames.set(null);
}
}
> 로그인 시 사용자ID를 ThreadLocal에 저장. security 프레임워크에서 Authentication Provider에 로직 추가
@Component
public class AccountAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
// anthoer private variables and method ......
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken token)
throws AuthenticationException {
if (token.getCredentials() == null || userDetails.getPassword() == null){
throw new BadCredentialsException("Credentials may not be null.");
}
if (!passwordEncoder.matches((String)token.getCredentials(), userDetails.getPassword())){
throw new BadCredentialsException("Invalid credentials");
}
RequestContext.setuserName(userDetails.getUsername());
}
}
> Abstracting Common Reference Data Attributes into a Mapped Supperclass
> Reference Entity Attributes
- id - the primary key
- code - a unique, alphanumeric identifier
- label - a brief description
- ordinal - a numeric value; facilitates soring
- effectiveAt - when the entity is available for use
- expriesAt - when the entity is no longer valid
- createdAt - when the entity was initially persisted
> reference data attributes에 해당하는 칼럼들 포함하는 테이블 구성
CREATE TABLE Role (
id BIGINT NOT NULL,
code VARCHAR(50) NOT NULL,
label VARCHAR(100) NOT NULL,
ordinal INT NOT NULL,
effectiveAt DATETIME NOT NULL,
expiresAt DATETIME DEFAULT NULL,
createdAt DATETIME NOT NULL,
PRIMARY KEY (id),
CONSTRAINT UQ_Role_Code UNIQUE (code)
);
> reference entity attribute 를 가지는 공통 부모 Entity Class 생성
> reference entity 클래스는 ReferenceEntity class 상속
> repository class for reference entity 클래스 생성
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
Collection<Role> findByEffecvieAtBeforeAndExpiredAtAfterOrExpiresAtNullOrderByOrdinal(Date effectiveAt, Date expiresAt);
@Query("select r from role where r.effectiveAt <= :effectiveAt and (r.expiresAt is null or. r.expriresAt > :effectiveAt) order by r.ordinal asc")
Collection<Role> findAllEffective(@Param("effectiveAt") Date effectiveAt);
Role findByCodeEffectiveAtBeforeAndExpiredsAtAfterOrExpiresAtNull(String code, Date effetiveAt, Date expiresAt);
@Query("select r from role where r.code = :code and r.effectiveAt <= :effetiveAt and (r.expiresAt is null or r.expiresAt > :effectiveAt")
Role findByCodeAndEffective(@Param("code") String code, @Param("effectiveAt") Date effectiveAt);
}
> controller class for reference entity 클래스 생성
@RestController
public class RoleController {
@Autowired
private RoleRepository roleRepository;
@RequestMapping(value = "/api/roles", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Collection<Role>> getRoles(
@RequestParam(value = "querytype", defaultValue = "annotation") String queryType) {
Collection<Role> roles = null;
Date now = new Date();
if (queryType.equals("method")){
roles = roleRepository.findByEffectiveAtBeforeAndExpiresAtAfterOrExpiresAtNullOrderByOrdinalAsc(now, now);
} else {
roles = roleRepository.findAllEffective(new Date());
}
return new ResponseEntity<Collection<Role>>(roles, HttpStatus.OK);
}
}
> Using JODA Framework Classes as JPA Entity Model Attributes
> pom.xml dependency 추가
> properties 추가. application.properties파일
spring.jpa.properties.jadira.usertype.autoRegisterUsertypes=true
> @MappedSuperclass 클래스의 java.util.Date 타입 properties를 org.joda.time.DateTime 타입으로 변경.
> AbstractControllerTest 클래스 내 ObjectMapper object에 jodatime 모듈 추가
> Exploring Spring Data JPA Query Definition Strateties
Understanding Query Definition Strategeis
> Query Method
: Spring Data JPA derives a SQL statement form the Repository interface method name.
- Uses camelCase for structure and keyword identification.
- Keyworkd 'find' indicates a SELECT statement.
- Words after 'By' are mapped to entity attributes.
- Seperate attributes with keywords 'And' / 'Or'.
- Refine with keywords: 'Like', 'IgnoreCase', 'Distinct', 'True', 'False', 'Before', 'After', 'Between', 'OrderBy' etc.
- 참고 : Query Keywords : http://docs.spring.io/spring-data/jpa/docs/1.10.1.RELEASE/reference/html/#repository-query-keywords
- Return type indicates if one or more rows is expected as the query results
- NoUniqueResult thrown if single row exprected, but not received
// select a from account a where a.username = ?
Account findByUsername(String username);
// select a
// from account a
// where upper(a.username) like upper('%' + ?l + '%')
// and a.enabled = true
// order by a.username ASC
Collection<Account> findByUsernameContainingIgnoreCaseAndEnabledTrueOrderByUsernameAsc(String username);
Account findByUsernameAndEnabledTrue(String username)
Account findByUsernameAndEnabled(String username, boolean enabled)
Account findByUsernameAndEnabledTrue(String username)
Collection<Account> findByUsernameLikeIgnoreCase(String username)
> @NamedQuery Annotation
- Spring Data JPA matches a named query to a Repository interface mthoed using the annotation's name element
@Entity
@NamedQueries({
@NamedQuery(name = "Account.findByUsernameNamedQuery",
query = "select a from Account a where a.username = ?1"),
@NamedQuery(name = "Account.findAllEnabledLikeUsernameNamedQuery",
query = "select a from Account a where UPPER(a.username) like UPPER(?1) and enabled = true order by a.username ASC")
})
public class Account extends TransactionalEntity{...}
======================================================================================
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
Account findByUsernameNamedQuery(String username);
Account findByUsernameNamedQuery(String username);
Collection<Account> findAllEnabledLikeUsernameNamedQuery(String username);
- Native queries may be created using the @NamedNativeQuery annotation; however, database portability is sacrificed.
query = "select * from Account where username = ?1")
public class Account extends TransactionalEntity{...}
> @Query Annotaion
- Spring Data JPA repository methods annotated with JPQL or native queries
- Simplified LIKE queries remove programmatic manipulation of query parameter values.
- Cannot be coupled with functions like UPPER(%?1%)
- Use the @Param annotaion to replace positional parameters with named parameters.
- Improves readability / maintainability
- Reduce errors from future refactoring.
> Using MySQL
> 데이타베이스 및 사용자 생성
> pom.xml dependency 추가
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
> application-mysql.properties 생성
# Connection
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/greeting
spring.datasource.username=greetusr
spring.datasource.password=greetpwd
spring.datasource.name=greeting
# Initialization
spring.datasource.schema=classpath:/data/mysql/schema.sql
spring.datasource.data=classpath:/data/mysql/data.sql
# Pool
spring.datasource.initial-size=10
spring.datasource.max-active=50
spring.datasource.min-idle=5
spring.datasource.max-idle=5
spring.datasource.test-on-borrow=true
spring.datasource.validation-query=select 1;
spring.datasource.time-between-eviction-runs-millis=60000
spring.datasource.min-evictable-idle-time-millis=300000
> application.properties 의 profile 설정
#spring.profiles.active=hsqldb,batch
spring.profiles.active=mysql
- Total
- Today
- Yesterday