diff --git a/spring-boot/src/main/java/com/mpc/springboot/Application.java b/spring-boot/src/main/java/com/mpc/springboot/Application.java index 2f9e68e..51b0be6 100644 --- a/spring-boot/src/main/java/com/mpc/springboot/Application.java +++ b/spring-boot/src/main/java/com/mpc/springboot/Application.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class Application { diff --git a/spring-boot/src/main/java/com/mpc/springboot/config/audit/SecurityAuditorAware.java b/spring-boot/src/main/java/com/mpc/springboot/config/audit/SecurityAuditorAware.java new file mode 100644 index 0000000..9a669fb --- /dev/null +++ b/spring-boot/src/main/java/com/mpc/springboot/config/audit/SecurityAuditorAware.java @@ -0,0 +1,15 @@ +package com.mpc.springboot.config.audit; + +import org.springframework.data.domain.AuditorAware; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class SecurityAuditorAware implements AuditorAware { + + @Override + public Optional getCurrentAuditor() { + return Optional.of("test-member"); + } +} diff --git a/spring-boot/src/main/java/com/mpc/springboot/config/persistence/jpa/QuotedCamelCaseToUnderScoresNamingStrategy.java b/spring-boot/src/main/java/com/mpc/springboot/config/persistence/jpa/QuotedCamelCaseToUnderScoresNamingStrategy.java new file mode 100644 index 0000000..3564b2c --- /dev/null +++ b/spring-boot/src/main/java/com/mpc/springboot/config/persistence/jpa/QuotedCamelCaseToUnderScoresNamingStrategy.java @@ -0,0 +1,26 @@ +package com.mpc.springboot.config.persistence.jpa; + +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; + +public class QuotedCamelCaseToUnderScoresNamingStrategy extends CamelCaseToUnderscoresNamingStrategy { + + @Override + public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) { + if (name == null) { + return null; + } + Identifier snakeCaseIdentifier = super.toPhysicalColumnName(name, jdbcEnvironment); + return Identifier.toIdentifier("`" + snakeCaseIdentifier.getText() + "`", snakeCaseIdentifier.isQuoted()); + } + + @Override + public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) { + if (name == null) { + return null; + } + Identifier snakeCaseIdentifier = super.toPhysicalTableName(name, jdbcEnvironment); + return Identifier.toIdentifier("`" + snakeCaseIdentifier.getText() + "`", snakeCaseIdentifier.isQuoted()); + } +} diff --git a/spring-boot/src/main/java/com/mpc/springboot/member/application/service/MemberService.java b/spring-boot/src/main/java/com/mpc/springboot/member/application/service/MemberService.java index c539d52..948efae 100644 --- a/spring-boot/src/main/java/com/mpc/springboot/member/application/service/MemberService.java +++ b/spring-boot/src/main/java/com/mpc/springboot/member/application/service/MemberService.java @@ -1,5 +1,7 @@ package com.mpc.springboot.member.application.service; +import jakarta.transaction.Transactional; + import org.springframework.stereotype.Service; import com.mpc.springboot.member.domain.entity.Member; import com.mpc.springboot.member.domain.exception.MemberNotFoundException; @@ -18,6 +20,7 @@ public Member getMemberBy(MemberCode code) { .orElseThrow(MemberNotFoundException::new); } + @Transactional public Member createMember(Member member) { return memberRepository.save(member); } diff --git a/spring-boot/src/main/java/com/mpc/springboot/member/domain/entity/Member.java b/spring-boot/src/main/java/com/mpc/springboot/member/domain/entity/Member.java index 4aa7237..d8e9c78 100644 --- a/spring-boot/src/main/java/com/mpc/springboot/member/domain/entity/Member.java +++ b/spring-boot/src/main/java/com/mpc/springboot/member/domain/entity/Member.java @@ -2,13 +2,16 @@ import jakarta.persistence.*; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; import com.mpc.springboot.member.domain.vo.MemberCode; import com.mpc.springboot.member.domain.mapping.MemberCodeConverter; import com.mpc.springboot.member.domain.vo.MemberName; +import com.mpc.springboot.shared.domain.AuditFields; import lombok.*; @Getter -@NoArgsConstructor +@EntityListeners(AuditingEntityListener.class) +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class Member { @@ -22,10 +25,8 @@ public class Member { @Embedded private MemberName name; - public Member(MemberCode code, MemberName name) { - this.code = code; - this.name = name; - } + @Embedded + private AuditFields auditFields = new AuditFields(); } diff --git a/spring-boot/src/main/java/com/mpc/springboot/member/infrastructure/persistence/jpa/repository/MemberJpaRepository.java b/spring-boot/src/main/java/com/mpc/springboot/member/infrastructure/persistence/jpa/repository/MemberJpaRepository.java index 4f4621c..a6d69e0 100644 --- a/spring-boot/src/main/java/com/mpc/springboot/member/infrastructure/persistence/jpa/repository/MemberJpaRepository.java +++ b/spring-boot/src/main/java/com/mpc/springboot/member/infrastructure/persistence/jpa/repository/MemberJpaRepository.java @@ -3,10 +3,12 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import com.mpc.springboot.member.domain.entity.Member; import com.mpc.springboot.member.domain.vo.MemberCode; -public interface MemberJpaRepository extends JpaRepository { +@Repository +public interface MemberJpaRepository extends JpaRepository { Optional findByCode(MemberCode code); } diff --git a/spring-boot/src/main/java/com/mpc/springboot/shared/domain/AuditFields.java b/spring-boot/src/main/java/com/mpc/springboot/shared/domain/AuditFields.java new file mode 100644 index 0000000..db76ac6 --- /dev/null +++ b/spring-boot/src/main/java/com/mpc/springboot/shared/domain/AuditFields.java @@ -0,0 +1,34 @@ +package com.mpc.springboot.shared.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; + + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +@Embeddable +public class AuditFields { + + @CreatedBy + @Column(updatable = false) + private String createdBy; + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdDateTime; + + @LastModifiedBy + private String lastModifiedBy; + + @LastModifiedDate + private LocalDateTime lastModifiedDateTime; +} diff --git a/spring-boot/src/main/resources/application.yml b/spring-boot/src/main/resources/application.yml index 6f767ec..125d4d8 100644 --- a/spring-boot/src/main/resources/application.yml +++ b/spring-boot/src/main/resources/application.yml @@ -9,6 +9,8 @@ spring: database: mysql hibernate: ddl-auto: validate + naming: + physical-strategy: com.mpc.springboot.config.persistence.jpa.QuotedCamelCaseToUnderScoresNamingStrategy show-sql: true properties: hibernate.format_sql: true diff --git a/spring-boot/src/main/resources/db/migration/V3__add_audit_fields.sql b/spring-boot/src/main/resources/db/migration/V3__add_audit_fields.sql new file mode 100644 index 0000000..7a28759 --- /dev/null +++ b/spring-boot/src/main/resources/db/migration/V3__add_audit_fields.sql @@ -0,0 +1,4 @@ +alter table member add column created_by varchar(320); +alter table member add column last_modified_by varchar(320); +alter table member add column created_date_time timestamp; +alter table member add column last_modified_date_time timestamp;