CREATE TABLE table_name(
column1 datatype UNIQUE KEY,
column2 datatype,
...
...
columnN datatype
);
ALTER TABLE table_name ADD CONSTRAINT UNIQUE_KEY_NAME UNIQUE(column_name);
CREATE TABLE table_name(
column1 datatype,
column2 datatype,
...
columnN datatype,
PRIMARY KEY(column_name)
);
ALTER TABLE table_name ADD CONSTRAINT PRIMARY KEY(column_name);
ALTER TABLE table_name DROP PRIMARY KEY;
CREATE TABLE table_name (
column1 datatype,
column2 datatype,
...
CONSTRAINT fk_name
FOREIGN KEY (column_name)
REFERENCES referenced_table(referenced_column)
);
ALTER TABLE TABLE2
ADD CONSTRAINT fk_name
FOREIGN KEY(column_name)
REFERENCES TABLE1(column_name);
ALTER TABLE table_name DROP FOREIGN KEY (constraint fk_name);
Index: special lookup tables used to speed up data retrieval
PRIMARY
& UNIQUE
constraintsCREATE UNIQUE INDEX index_name
on table_name (column_name);
CREATE INDEX index_name
ON table_naem (column_name);
CREATE INDEX index_name
on table_name (column1, column2);
CREATE INDEX index_name ON table_name;
DROP INDEX index_name;
SHOW INDEX FROM table_name;
Transaction: unit of work performed on a database
ACID는 관계형 데이터베이스 트랜잭션에 필수적인 4가지 요소다
Consistent: integrity constraints are maintained
COMMIT
- save changesCOMMIT;
ROLLBACK
- undo transactions that are not already saved to databaseROLLBACK;
SAVEPOINT
- creates points within groups of transactions to which to ROLLBACK
SAVEPOINT SAVEPOINT_NAME;
...
ROLLBACK TO SAVEPOINT_NAME;
...
RELEASE SAVEPOINT SAVEPOINT_NAME
SET TRANSACTION
- places name on a transaction SET TRANSACTION [READ WRITE | READ ONLY];
ORM: Object-Relational Mapping
객체지향적 개념의 클래스와 관계형 데이터베이스를 매핑하는 개념
ORM 덕에 어플리케이션의 객체를 관계형 데이터베이스의 테이블로 자동 매핑 및 영속화가 된다
장점
단점
JPA: Java Persistence API
Java 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 Interface
[이미지 출처]
spring-data-jpa
src/main/resources/application.yml
spring:
jpa:
hibernate:
ddl-auto: create
properties:
hibernate.dialect: #본인이 사용할 데이터베이스
...
logging:
level:
org.hibernate.SQL: debug
ddl-auto:create
: 테이블 자동 생성update
값을 사용하면 테이블의 변화를 생겼을 때 hibernate이 변화를 자연스럽게 반영하지 못할 가능성이 존재하기 때문에 create
을 사용하는 편org.hibernate.SQL: debug
: JPA/hibernate이 생성하는 SQL이 logger를 통해 보임@Entity
@Getter @Setter
@Table(name = "USER",
indexes = {
@Index(name = "username", columnList = "username"),
@Index(name = "email", columnList = "email")
})
public class User {
@Id
@GeneratedValue
private Long id;
@NotBlank(message = "사용자 이름은 필수 입력 항목입니다.")
@Size(min = 4, max = 12)
@IncludeCharInt
private String username;
@NotBlank(message = "암호는 필수 입력 항목입니다.")
@Size(min = 8)
private String password;
@NotBlank(message = "이름은 필수 입력 항목입니다.")
private String name;
@Email
@NotBlank(message = "이메일은 필수 입력 항목입니다.")
private String email;
@NotBlank(message = "전화번호 필수 입력 항목입니다.")
@Pattern(regexp = "^01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$")
private String mobileNumber;
@NotBlank(message = "전공은 필수 입력 항목입니다.")
private String major;
@NotNull(message = "학번은 필수 입력 항목입니다.")
private int studentId;
@NotBlank(message = "자기소개는 필수 입력 항목입니다.")
private String description;
@Enumerated(EnumType.STRING)
private SchoolStatus schoolStatus;
private boolean isMember;
private boolean isClubMember;
private boolean isAdmin;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Activity> leadingSeminars = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Activity> leadingStudies = new ArrayList<>();
@OneToMany(mappedBy = "user")
private Set<Participation> participationList = new HashSet<>();
public User(String username, String password, String name, String email, String mobileNumber, String major, int studentId, String description) {
this.username = username;
this.password = password;
this.name = name;
this.email = email;
this.mobileNumber = mobileNumber;
this.major = major;
this.studentId = studentId;
this.description = description;
this.schoolStatus = SchoolStatus.DEFAULT;
this.isMember = true;
this.isClubMember = false;
this.isAdmin = false;
}
public User() {
this.isMember = true;
this.isAdmin = false;
this.isClubMember = false;
}
...
}
isMember
, isAdmin
, isClubMember
는 역할을 따로 테이블을 만들어 관리하는 것이 좋을까? 아니면 boolean
값으로 관리하는 것이 좋을까?@NotBlank
, @NotEmpty
, @NotNull
는 어떻게 다를까?@Entity
@Getter @Setter
public class Participation {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "activity_id")
private Activity activity;
private boolean isApproved;
private String reason;
...
}
인터페이스 먼저 정의
@Target( { ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = IncludeCharIntValidator.class)
public @interface IncludeCharInt {
public String message() default "Your username must include both English characters and numbers";
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default {};
}
구현 클래스 정의
public class IncludeCharIntValidator implements ConstraintValidator<IncludeCharInt, String> {
char[] chars;
@Override
public void initialize(IncludeCharInt constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
chars = value.toCharArray();
int strCount = 0;
int intCount = 0;
for (char c : chars) {
if (!Character.isLetter(c) && !Character.isDigit(c)) {
return false;
}
if (Character.isLetter(c)) strCount++;
if (Character.isDigit(c)) intCount++;
}
if (strCount==0 || intCount==0) {
return false;
}
return true;
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long>, PagingAndSortingRepository<User, Long>, JpaSpecificationExecutor<User> {
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
List<User> findAll();
Page<User> findByUsernameContaining(String infix, Pageable pageable);
Page<User> findByNameContaining(String infix, Pageable pageable);
Page<User> findByMajorContaining(String infix, Pageable pageable);
Page<User> findByIsClubMemberTrue(Pageable pageable);
Page<User> findByIsAdminTrue(Pageable pageable);
}
findById()
, deleteById()
와 같은 메서드는 제공됨Page
등을 사용하여 요청된 데이터의 크기가 클 경우를 대비한다전체 코드는 제 깃허브 레포에 있어요
References: