Spring_FrameWork

DB연동

tmxhsk99 2019. 3. 22. 17:21

JDBC 프로그래밍을 하다보면 중복되는 코드가 많다 .

반복되는 줄이려고 탬플릿 메서드 패턴과 전략패턴을 사용해서 

스프링이 만든것이 JdbcTemplate이다 이것을 활용하면 코드를 줄일수 있음 


스프링으로 DB연동을 처리할때 얻을 수 있는 장점은 트랜잭견부분이다.

JDBC API를 사용할 경우 , 트랜잭션을 처리하려면 다음과 같이 Connection 의 setAutoCommit()을 이용해서 자동 커밋을 비활성화 하고 

commit() 과 rollback() 메서드를 이용해서 트랜잭션을 커밋하거나 롤백해야한다,


public void insert(Member member){

Connetion conn = null;

PreparedStatement = null;

try{

conn = DriverManager.getConnection("jdbc:mysql://localhost/sprig4fs?characterEncoding=utf8","id","passward");

conn.setAutoCommit(false)

....

conn.commit();

}catch(SQLException ex){

if(conn!=null)

try{conn.rollback();}catch(SQLException e){}

}finally(){

if(pstmt!=null){

try{pstmt.colse();}catch(SQLException e){}

}

if(conn!=null){

try{conn.colse();}catch(SQLException e){}

}

}

}

스프링을 사용하면 트랜잭션을 적용하고 싶은 메서드에 @Transactional 애노테이션을 붙이기만 하면 된다.


pom.xml설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>sp4</groupId>
    <artifactId>sp4-chap08</artifactId>
    <version>0.0.1-SNAPSHOT</version>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.2.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.30</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
</project>
cs


mysql 스키마 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
create user 'spring4'@'localhost' identified by 'spring4';
 
create database spring4fs character set=utf8;
 
grant all privileges on spring4fs.* to 'spring4'@'localhost';
 
create table spring4fs.MEMBER (
    ID int auto_increment primary key,
    EMAIL varchar(255),
    PASSWORD varchar(100),
    NAME varchar(100),
    REGDATE datetime,
    unique key (EMAIL) 
engine=InnoDB character set = utf8;
cs


1행 mysql 사용자 spring4 계정생성 (암호로 spring4 사용)

3행 spring4fs DB 생성

5행 spring4fs DB에 spring4 계정이 접근할 수 있도록 권한 부여

07 - 14 행 spring4fs DB에 Member테이블 생성 캐릭터셋은 utf8로 설정


그리고 예제에서 사용할 데이터를 미리 넣어둔다

insert into MEMBER(EMAIL, PASSWORD, NAME, REGDATE)

values('aaa@aaa.net','1234','kim',now());


03 DataSource 설정



JDBC API 는 DataSource를 이용해서 DB를 연결을 구하는 방법을 정의 하고 있다. 

DataSoruce를 사용하면 다음과 같은 방식으로 Connection을 구할 수 있다. 


스프링이 제공하는 DB 연동기능도 DataSource를 사용해서 DBConnection 을 구할 수 있도록 구현 되어 있다.

DB연동에 사용할 DataSource를 스프링 빈으로 등록하고

DB연동 기능을 구현한 빈객체는 DataSource를 주입받아 사용한다.


DataSource 기능을 제공하는 모듈로는 c3p0 와 dbcp 등이 존재하고, 

스프링 테스트 목적으로 사용할 수 있는 DataSource 구현을 제공하고 있다.

c3p0 모듈이 제공하는 DataSource 구현을 사용한다.


c3p0모듈은 DataSource를 구현한 ComboPooledDataSource 클래스를 제공하고 있으므로, 이 클래스를 스프링 빈으로 등록해서 DataSource로 사용하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring4fs?characterEncoding=utf8" />
        <property name="user" value="spring4" />
        <property name="password" value="spring4" />
    </bean>
 
</beans>
cs


driverClass : JDBC 드라이버 클래스를 지정한다. 위설정은 MYSQL 

jdbcUrl :  JDBC URL 지정 , 데이터베이스와 테이블의 캐릭터 셋을 UTF-8로 설정 했으므로 JDBC URL의 caracterEncoding 파라미터를 이용해서 MYSQL에 연결할 사용할 캐릭터 셋을 UTF-8로 지정


user : 사용자 계정 지정

password : DB 연결할 때 사용할 암호 지정


close : 커넥션 풀에 보관된 Connection을 종료할 목적으로 사용됨.


04 JdbcTemplate 을 이용한 쿼리 실행


DataSource를 설정했다면 스프링이 제공하는 JdbcTemplate 클래스를 이용해서 DB를 연동 처리 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package spring;
 
import java.util.List;
 
import javax.sql.DataSource;
 
import org.springframework.jdbc.core.JdbcTemplate;
 
public class MemberDao {
    private JdbcTemplate jdbcTemplate;
    
    
 
    public MemberDao(DataSource dataSource) {
        super();
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
cs


먼저 dataSource를 주입받도록 설정한다. 


그리고 스프링에 MemberDao 클래스에 추가 했으므로 스프링 설정에 MemberDao 빈설정을 추가한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring4fs?characterEncoding=utf8" />
        <property name="user" value="spring4" />
        <property name="password" value="spring4" />
    </bean>
    
    <bean id"memberDao" class="spring.MemberDao">
        <constructor-arg ref="dataSource"/>
    </bean>
 
</beans>
cs



JDBCTemplate를 이용한 조회 쿼리 실행 


JdbcTemplate 클래스 SELECT 쿼리 실행을 위한 query() 메서드를 제공하고 있다.

그중에서 자주 사용되는 쿼리 메소드

- List<T> query(String sql, RowMapper<T> rowMapper)
- List<T> query(String sql, Object[] args,RowMapper<T> rowMapper)
- List<T> query(String sql, RowMapper<T> rowMapper, Object...args)

sql파라미터로 전달받은 쿼리를 실행하고 rowmapper를 이용해서 ResultSet의 결과를 자바 객체로 변환한다.
sql 파라미터가 인덱스 기반 파라미터 (PreparedStatement 의 물음표) 를 가진 경우 , args 파라미터를 이용해서 각 인텍스 파라미터를 지정한다.


쿼리 실행결과를 변환하는 RowMapper 인터페이스는 다음과 같이 정의 되어있다.

public interface RowMapper<T>{
T mapRow(ResultSet rs, int rowNum)throws SQLException;
}

RowMapper의 mapRow() 메서드 SQL 실행결과로 구한 ResultSet으로부터 한 행의 데이터를 읽어와 이를 자바 객체로 변환해 주는 매퍼 기능을 구현한다.


한번 해보자 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package spring;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
 
import javax.sql.DataSource;
 
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
 
public class MemberDao {
    private JdbcTemplate jdbcTemplate;
    
 
 
 
    public MemberDao(DataSource dataSource) {
        super();
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
 
    public Member selectByEmail(String email) {
        //로우매퍼 인터페이스 구현
        RowMapper<Member> rm=new RowMapper<Member>() {
 
            @Override
            public Member mapRow(ResultSet rs,int rowNum) throws SQLException {
                Member member = new Member(rs.getString("EMAIL"),
                        rs.getString("PASSWORD"),
                        rs.getString("NAME"),
                        rs.getTimestamp("REGDATE"));
                member.setId(rs.getLong("ID"));
                return member;
            }
        };
        //jdbc탬플레이트에 query 실행 
        List<Member> result = jdbcTemplate.query("select *from MEMBER where EMAIL =?", rm,email);
        //없으면 null 반환 있으면 리스트의  반환 
        return result.isEmpty() ? null : result.get(0);
    }
 
 
}
 
cs

selectAll도 구현 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    public List<Member> selectAll() {
        List<Member> results = jdbcTemplate.query("select * from MEMBER"
                new RowMapper<Member>() {
 
                    @Override
                    public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
                        Member member= new Member(rs.getString("EMAIL"),
                                rs.getString("PASSWORD"),
                                rs.getString("NAME"),
                                rs.getTimestamp("REGDATE"));
                        member.setId(rs.getLong("ID"));
                        return member;
                    }
            });
        return results;
    }
 
cs

결과가 1행인 경우 조회 메서드 : queryForObject()


만약에 카운트 같은 쿼리는 정수 타입으로 바로 받는 게 편하다 

1
2
3
4
    public int count() {
        Integer count = jdbcTemplate.queryForObject("select count(*) from MEMBER", Integer.class);
        return count;
    }
cs


queryforObject의 주의점은 결과는 반드시 한 행

그 이상이면 오류가 발생한다.

정확히 1개가 아니라면 query 메서드를 사용해야한다.


JDBCTemplate을 이용한 변경 쿼리 실행 


INSERT, UPDATE, DELETE 쿼리를 실행할 때에는 다음의 update()메서드를 사용하면된다.

- int update(String sql)
- int update(String sql,Object...args)

update 사용예

1
2
3
4
public void update(Member member) {
        jdbcTemplate.update("update MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?",
                member.getName(), member.getPassword(), member.getEmail());
    }
cs



PreparedStatementCreator를 이용한 쿼리 실행 


지금 까지 코드는 쿼리에서 사용할 값을 인자로 전달해 주었다.

1
2
3
4
public void update(Member member) {
        jdbcTemplate.update("update MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?",
                member.getName(), member.getPassword(), member.getEmail());
    }
cs


경우에 따라 set 메서드를 사용해서 직접 인덱스 파라미터 값을 설정해 주어야 할 때가 있다.

이런 경우에는 PreparedStatementCreator를 인자로 받는 메서드를 이용해서 직접 PreparedStatement를 생성하고 설정해 주어야한다.


jdbc.Template,update(new PreparedStatementCreator(){

@Override

public PreparedStatement createPreparedStatement(Connection con)throws SQLException{

//파라미터로 전달받은 Connection 을 이용해서 PreparedStatement를  생성

PreparedStatement pstmt = con.preapeStatement("insert into MEMBER(EMAIL, PASSWORD, NAME, REGDATE)values (?,?,?,?)");

// 인덱스 파라미터 값 설정

pstmt.setString(1,member.getEmail());

pstmt.setString(2,member.getPasswordl());

pstmt.setString(3,member.getName());

pstmt.setTimestamp(4,new Timestamp(member.getRegisterDate().getTime()));

//생성한 pstmt 리턴

return pstmt

}

});


INSERT 쿼리 실행시 , KeyHolder를 이용해서 자동 생성 키 값을 구하기


오토 인크리먼트같은 자동 증가 칼럼테이블에 값을 삽입할 경우 , 해당 칼럼의 값이 자동으로 생성된다.

보통 INSERT 문에는 increment 하는 값을 넣지 않음
쿼리 실행후 생성된 키값을 알고 싶다면 
KeyHolder를 사용하면 된다.
MemberDao insert()메서드를 구현 할 때 삽입되는 Member 객체의 ID 값을 구할수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection con) 
                    throws SQLException {
                PreparedStatement pstmt = con.prepareStatement(
                        "insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) "+
                        "values (?, ?, ?, ?)",
                        new String[] {"ID"});
                pstmt.setString(1,  member.getEmail());
                pstmt.setString(2,  member.getPassword());
                pstmt.setString(3,  member.getName());
                pstmt.setTimestamp(4,  
                        new Timestamp(member.getRegisterDate().getTime()));
                return pstmt;
            }
        }, keyHolder);
        Number keyValue = keyHolder.getKey();
        member.setId(keyValue.longValue());
cs


 PreparedStatement pstmt = con.prepareStatement(
                        "insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE) "+
                        "values (?, ?, ?, ?)",
                        new String[] {"ID"});

여기에서 두번째 파라미터가 자동증가 칼럼이므로 아이디를줌


update()메서드 는 PreaparedStatement를 실행후 , 자동 생성된 키값을 KeyHolder에 보관한다.

보관된 키는 getKey 메서드로 구할 수있다.

Number로 받은후 원하는 타입으로 변경해서 사용한다.

다음코드는 long타입으로 변환

 Number keyValue = keyHolder.getKey();
        member.setId(keyValue.longValue());


05 MemberDAO 테스트

메인 클래스 작성하자 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
 
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
 
import spring.Member;
import spring.MemberDao;
 
public class MainForMemberDao {
 
    private static MemberDao memberDao;
 
    public static void main(String[] args) {
        AbstractApplicationContext ctx = 
                new GenericXmlApplicationContext("classpath:appCtx.xml");
 
        memberDao = ctx.getBean("memberDao", MemberDao.class);
 
        selectAll();
        updateMember();
        insertMember();
 
        ctx.close();
    }
 
    private static void selectAll() {
        System.out.println("----- selectAll");
        int total = memberDao.count();
        System.out.println("전체 데이터: " + total);
        List<Member> members = memberDao.selectAll();
        for (Member m : members) {
            System.out.println(m.getId() + ":" + m.getEmail() + ":" + m.getName());
        }
    }
 
    private static void updateMember() {
        System.out.println("----- updateMember");
        Member member = memberDao.selectByEmail("aaa@aaa.net");
        String oldPw = member.getPassword();
        String newPw = Double.toHexString(Math.random());
        member.changePassword(oldPw, newPw);
 
        memberDao.update(member);
        System.out.println("암호 변경: " + oldPw + " > " + newPw);
    }
 
    private static void insertMember() {
        System.out.println("----- insertMember");
        SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmss");
        String prefix = dateFormat.format(new Date());
        System.out.println("prefix :"+prefix);
        Member member = 
                new Member(prefix + "@test.com", prefix, prefix, new Date());
        memberDao.insert(member);
        System.out.println(member.getId() + " 데이터 추가");
    }
 
}
cs

트랜잭션 처리 

두개이상의 쿼리를 한번의 작업에 서 실행해야할때 


@Transaction을 이용한 트랜잭션 처리 

트랜잭션범위를 지정 가능한 애노테이션
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package spring;
 
import org.springframework.transaction.annotation.Transactional;
 
public class ChangePasswordService {
 
    private MemberDao memberDao;
 
    public ChangePasswordService(MemberDao memberDao) {
        this.memberDao = memberDao;
    }
 
    @Transactional
    public void changePassword(String email, String oldPwd, String newPwd) {
        Member member = memberDao.selectByEmail(email);
        if (member == null)
            throw new MemberNotFoundException();
        
        member.changePassword(oldPwd, newPwd);
        
        memberDao.update(member);
    }
}
 
cs

chagePassword() 메서드에서 실행되는 코드를 하나의 트랜잭션 범위에서 실행한다.
따라서, memberDao,selectByEmail()에서 실행하는 쿼리와 
member.changePassword() 에서 실행하는 쿼리가 한트랜잭션에서 묶여서 실행된다.

@Transactional 애노테이션이 제대로 동작하려면 다음의 두가지 내용을 스프링 설정에 추가해야한다.
1. PlatformTransactionManager 빈 설정
2. @Transactional 애노테이션 활성화 설정 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
 
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost/spring4fs?characterEncoding=utf8" />
        <property name="user" value="spring4" />
        <property name="password" value="spring4" />
    </bean>
    
    <bean id = "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <bean id= "memberDao" class="spring.MemberDao">
        <constructor-arg ref="dataSource"/>
    </bean>
 
</beans>
cs


PlatformTransactionManager는 스프링이 제공하는 트랜잭션 관리자를 위한 인터페이스이다.
스프링으 구현 기술에 상관없이 동일한 방식으로 트랜잭션을 처리하기 위해  이 인터페이스를 사용함

JDBC 연동을 사용하는 경우 DataSourceTracsactionManager클래스를  


PlatformTransactionManager로 사용한 경우다.

위설정은 dataSource를 프로퍼티의 값으로 이용해서 트래잭션 연동에 사용할 DataSource를 지정한다.


<tx:annotation-driven> 태그는 @Transactional  애노태이션을 실행하는 기능을 활성화 .

transaction-manager 속성을 사용해서 트랜잭션을 수행할때 사용할 PlatformTransactionManager  빈을 지정한다.

앞서는 transactionManger 빈을 설정 했다.


트랜잭션 처리를 위한 설정을 완료하면 , 트랜잭션 범위에서 실행하고 싶은 스프링 빈 객체의 메서두애 @Transactional 애노테이션을 붙이면 된다.


chagePwdSvc를 실행할 메인클래스를 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main;
 
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
 
import spring.ChangePasswordService;
import spring.IdPasswordNotMatchingException;
import spring.MemberNotFoundException;
 
public class MainForCPS {
 
    public static void main(String[] args) {
        AbstractApplicationContext ctx =
                new GenericXmlApplicationContext("classpath:appCtx.xml");
 
        ChangePasswordService cps = 
                ctx.getBean("changePwdSvc", ChangePasswordService.class);
        try {
            cps.changePassword("aaa@aaa.net""1234""1111");
            System.out.println("암호를 변경했습니다.");
        } catch (MemberNotFoundException e) {
            System.out.println("회원 데이터가 존재하지 않습니다.");
        } catch (IdPasswordNotMatchingException e) {
            System.out.println("암호가 올바르지 않습니다.");
        }
 
        ctx.close();
    }
 
}
 
cs

Log를 보기 위해 Log4j를 사용해 보자 

pom.xml에 log4j.xml추가

1
2
3
4
5
    <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
cs

메이븐 업데이트 후 

로그를 어떤형식으로 어디에 기록할지 에 대한 설정 파일로 부터 읽어온다.

src/main/resources 

log4j.xml 에 다음과 같이 설정 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" 
                value="[%t] [%d{yyyy-MM-dd HH:mm:ss}] %-5p %c:%M - %m%n" />
        </layout>
    </appender>
 
    <root>
        <priority value="INFO" />
        <appender-ref ref="console" />
    </root>
 
    <logger name="org.springframework.jdbc">
        <level value="DEBUG" />
    </logger>
</log4j:configuration>
cs

로그의 디버그 레벨을 보기위한 설정


메인을 실행했더니

<bean id="changePwdSvc" class="spring.ChangePasswordService">

<constructor-arg ref="memberDao"/>

</bean>

빈등록을 안했었음


아까 비번이 랜덤으로 바뀌어있을 것이므로

비민번호를 "1234"로 변경후

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
log4j:WARN Continuable parsing error 21 and column 23
log4j:WARN 요소 유형 "log4j:configuration"의 콘텐츠는 "(renderer*,throwableRenderer?,appender*,plugin*,(category|logger)*,root?,(categoryFactory|loggerFactory)?)"과(와) 일치해야 합니다.
[main] [2019-03-26 03:10:06] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader:loadBeanDefinitions - Loading XML bean definitions from class path resource [appCtx.xml]
[main] [2019-03-26 03:10:06] INFO  org.springframework.context.support.GenericXmlApplicationContext:prepareRefresh - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1376c05c: startup date [Tue Mar 26 03:10:06 KST 2019]; root of context hierarchy
[main] [2019-03-26 03:10:06] INFO  com.mchange.v2.log.MLog:<clinit> - MLog clients using log4j logging.
[main] [2019-03-26 03:10:07] INFO  com.mchange.v2.c3p0.C3P0Registry:banner - Initializing c3p0-0.9.2.1 [built 20-March-2013 10:47:27 +0000; debug? true; trace: 10]
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:getTransaction - Creating new transaction with name [spring.ChangePasswordService.changePassword]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[main] [2019-03-26 03:10:07] INFO  com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource:getPoolManager - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge4n6a11eiedp6vm1k2z|462d5aee, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge4n6a11eiedp6vm1k2z|462d5aee, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost/spring4fs?characterEncoding=utf8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:doBegin - Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6ae5aa72] for JDBC transaction
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:doBegin - Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6ae5aa72] to manual commit
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.core.JdbcTemplate:query - Executing prepared SQL query
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.core.JdbcTemplate:execute - Executing prepared SQL statement [select *from MEMBER where EMAIL =?]
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.core.JdbcTemplate:update - Executing prepared SQL update
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.core.JdbcTemplate:execute - Executing prepared SQL statement [update MEMBER set NAME = ?, PASSWORD = ? where EMAIL = ?]
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.core.JdbcTemplate:doInPreparedStatement - SQL update affected 1 rows
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:processCommit - Initiating transaction commit
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:doCommit - Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6ae5aa72]
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:doCleanupAfterCompletion - Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6ae5aa72] after transaction
[main] [2019-03-26 03:10:07] DEBUG org.springframework.jdbc.datasource.DataSourceUtils:doReleaseConnection - Returning JDBC Connection to DataSource
암호를 변경했습니다.
[main] [2019-03-26 03:10:07] INFO  org.springframework.context.support.GenericXmlApplicationContext:doClose - Closing org.springframework.context.support.GenericXmlApplicationContext@1376c05c: startup date [Tue Mar 26 03:10:06 KST 2019]; root of context hierarchy
 
cs

진행하면 된다.