100일 챌린지/빅데이터기반 인공지능 융합 서비스 개발자

Day 45 - spring ver 2.x 문법 프로젝트 완성형

ksyke 2024. 9. 27. 12:31

Java 프로젝트 만들기

버전 업데이트

/pom.xml

<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>com.gimhae</groupId>
  <artifactId>day45_1</artifactId>
  <version>0.0.1-SNAPSHOT</version> 
  <properties>
  <spring-version>3.1.4.RELEASE</spring-version>
  </properties>
  <dependencies>
  <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-version}</version>
    </dependency>
  <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring-version}</version>
    </dependency>
  <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring-version}</version>
    </dependency>
  <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

  </dependencies> 
  <build>
    <finalName>day44</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
  </build>
</project>

Models

EmpVo

package com.gimhae.model;

public class EmpVo {
    private int empno,pay;
    private String ename;
    private LocalDate hiredate;
    public EmpVo() {
    }
    static class Builder{
        private int empno,pay;
        private String ename;
        private LocalDate hiredate;
        public Builder empno(int empno) {
            this.empno=empno;
            return this;
        }
        public Builder pay(int pay) {
            this.pay=pay;
            return this;
        }
        public Builder ename(String ename) {
            this.ename=ename;
            return this;
        }
        public Builder hiredate(java.sql.Date temporal) {
            this.hiredate=LocalDate.from(temporal.toLocalDate());
            return this;
        }
        public EmpVo build() {
            return new EmpVo(this);
        }
    }
    public EmpVo(Builder builder) {
        this.empno=builder.empno;
        this.ename=builder.ename;
        this.pay=builder.pay;
        this.hiredate=builder.hiredate;
    }
    public static Builder Builder() {
        return new Builder();
    }
    public int getEmpno() {
        return empno;
    }
    public void setEmpno(int empno) {
        this.empno = empno;
    }
    public int getPay() {
        return pay;
    }
    public void setPay(int pay) {
        this.pay = pay;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public LocalDate getHiredate() {
        return hiredate;
    }
    public void setHiredate(LocalDate hiredate) {
        this.hiredate = hiredate;
    }
    @Override
    public String toString() {
        return "EmpVo [empno=" + empno + ", pay=" + pay + ", ename=" + ename + "]";
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + empno;
        result = prime * result + ((ename == null) ? 0 : ename.hashCode());
        result = prime * result + pay;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        EmpVo other = (EmpVo) obj;
        if (empno != other.empno)
            return false;
        if (ename == null) {
            if (other.ename != null)
                return false;
        } else if (!ename.equals(other.ename))
            return false;
        if (pay != other.pay)
            return false;
        return true;
    }
}

EmpDao (Interface)

package com.gimhae.model;

public interface EmpDao {
    List<EmpVo> pullList();
    EmpVo getList(int idx);
    int addList(EmpVo bean);
    int setList(EmpVo bean);
    int rmList(int idx);
    int listSize();
}

EmpDaoImpl

package com.gimhae.model;

public class EmpDaoImpl implements EmpDao {
    JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    RowMapper<EmpVo> rowMapper;

    public EmpDaoImpl() {
        rowMapper=new RowMapper<EmpVo>() {

            @Override
            public EmpVo mapRow(ResultSet rs, int rowNum) throws SQLException {
                return EmpVo.Builder()
                        .empno(rs.getInt("empno"))
                        .ename(rs.getString("ename"))
                        .pay(rs.getInt("pay"))
                        .hiredate(rs.getDate("hiredate"))
                        .build();
            }
        };

    }

    @Override
    public List<EmpVo> pullList() {
        String sql="select * from emp38 order by empno";
        return jdbcTemplate.query(sql, rowMapper);
    }

    @Override
    public EmpVo getList(int idx) {
        String sql="select * from emp38 where empno=?";
        return jdbcTemplate.queryForObject(sql, rowMapper,idx);
    }

    @Override
    public int addList(EmpVo bean) {
        String sql="insert into emp38 (ename,pay,hiredate) values (?,?,now())";
        return jdbcTemplate.update(sql,bean.getEname(),bean.getPay());
    }

    @Override
    public int setList(EmpVo bean) {
        String sql="update emp38 set ename=?,pay=? where empno=?";
        return jdbcTemplate.update(sql,bean.getEname(),bean.getPay(),bean.getEmpno());
    }
    @Override
    public int rmList(int idx) {
        String sql="delete from emp38 where empno=?";
        return jdbcTemplate.update(sql,idx);
    }

    @Override
    public int listSize() {
        String sql="select count(*) as cnt from emp38";
        return jdbcTemplate.queryForInt(sql);
    }

}

/main/resources

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="com.mysql.cj.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/xe"
        p:username="scott" p:password="tiger"
    />
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource"
    />
    <bean id="empDao"
        class="com.gimhae.model.EmpDaoImpl"
        p:jdbcTemplate-ref="jdbcTemplate"
    />

</beans>

log4j.properties

log4j.rootLogger=WARN, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%c - %m%n

# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.gimhae.model=debug

Test cases

EmpDaoTest.java

package com.gimhae.model;

public class EmpDaoTest {
    static ApplicationContext ac;
    EmpDao empDao;
    EmpVo target;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        ac=new ClassPathXmlApplicationContext("/applicationContext.xml");
    }

    @Before
    public void setUp() throws Exception {
        empDao=ac.getBean(EmpDao.class);
        target=EmpVo.Builder().empno(4).pay(4444).ename("tester004").build();
    }

    @Test
    public void testPullList() {
        List<EmpVo> list=empDao.pullList();
        assertNotNull(list);
        assertTrue(list.size()>0);
    }
    @Test
    public void testGetlList() {
        assertEquals(target, empDao.getList(target.getEmpno()));
    }
    @Test
    public void testAddlList() {
        target.setEname("new1");
        target.setPay(1111);
        assertSame(1,empDao.addList(target));
    }
    @Test
    public void testListSize() {
        assertNotSame(0,empDao.listSize());
    }
    @Test
    public void testSetList() {
        target.setEname("tester4");
        assertSame(1,empDao.setList(target));
    }
    @Test
    public void testRmList() {
        assertSame(1,empDao.rmList(34));
    }

}

Log messgage 출력하기

ArroundLog.java

package com.gimhae.aop;

public class AroundLog implements MethodInterceptor {
    Logger log;

    @Override
    public Object invoke(MethodInvocation ivc) throws Throwable {
        log=Logger.getLogger(ivc.getThis().getClass());
        Object[] args=ivc.getArguments();
        log.debug("params:"+Arrays.toString(args));
        Object obj=ivc.proceed();
        log.debug("return:"+obj);
        return obj;
    }
}

applicationContext.xml

    ...
    <bean id="advice"
        class="com.gimhae.aop.AroundLog"
    />

    <bean id="pointcut"
        class="org.springframework.aop.support.NameMatchMethodPointcut">
        <property name="mappedNames">
            <list>
                <value>pullList</value>
            </list>
        </property>
    </bean>

    <bean id="pointcutAdvisor"
        class="org.springframework.aop.support.DefaultPointcutAdvisor"
        p:pointcut-ref="pointcut"
        p:advice-ref="advice"
    />

    <bean 
        class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
        p:beanNames="empDao"
        p:interceptorNames="pointcutAdvisor"
    />
    ...

Transaction

2개 이상의 query 문장이 실행될 떄, 동시에 실행되거나, 한 문장이 실패할떄 발생하는 문제를 없애기 위해 transaction이 사용된다.

EmpDaoImpl

public class EmpDaoImpl2 implements EmpDao {
    JdbcTemplate jdbcTemplate;
    PlatformTransactionManager transactionManager;
    ...
}
    ...
    @Override
    public List<EmpVo> pullList() {
        String sql="select * from emp38 order by empno";
        PreparedStatementCreator psc=null;
        psc=new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                PreparedStatement pstmt=con.prepareStatement(sql);
                return pstmt;
            }
        };
        return jdbcTemplate.query(psc, rowMapper);
    }

    @Override
    public EmpVo getList(int idx) {
        String sql="select * from emp38 where empno=?";
        return jdbcTemplate.queryForObject(sql, rowMapper,idx);
    }

    @Override
    public int addList(EmpVo bean) {
        String sql="insert into emp38 (ename,pay,hiredate) values (?,?,now())";
        String sql2="insert into emp38 (empno,ename,pay,hiredate) values (4,?,?,now())";

        TransactionDefinition definition=new DefaultTransactionDefinition();
        TransactionStatus status=transactionManager.getTransaction(definition);
        try {
        PreparedStatementCreator psc=null;
        psc=new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                System.out.println(con.hashCode());
                PreparedStatement pstmt=con.prepareStatement(sql);
                pstmt.setString(1, bean.getEname());
                pstmt.setInt(2, bean.getPay());
                return pstmt;
            }
        };
        jdbcTemplate.update(psc);
        psc=new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                System.out.println(con.hashCode());
                PreparedStatement pstmt=con.prepareStatement(sql2);
                pstmt.setString(1, bean.getEname());
                pstmt.setInt(2, bean.getPay());
                return pstmt;
            }
        };
        int result=jdbcTemplate.update(psc);
        transactionManager.commit(status);
        return result;
        }catch(Exception e) {
            transactionManager.rollback(status);
            return 0;
        }
    }
    ...
}

혹은

simpler version:
public class EmpDaoImpl implements EmpDao {
    JdbcTemplate jdbcTemplate;
    PlatformTransactionManager transactionManager;
    TransactionDefinition definition;
    ...
}
    ...
    @Override
    public int addList(EmpVo bean) {
        String sql="insert into emp38 (ename,pay,hiredate) values (?,?,now())";
        int result=0;

        TransactionStatus status=transactionManager.getTransaction(definition);
        try {
            result=jdbcTemplate.update(sql,bean.getEname(),bean.getPay());
            transactionManager.commit(status);
        }catch (Exception e) {
            transactionManager.rollback(status);
        }
        return result;
    }
    ...

applicationContext.xml

    ...
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource"
    />
    <bean id="empDao"
        class="com.gimhae.model.EmpDaoImpl2"
        p:jdbcTemplate-ref="jdbcTemplate"
        p:transactionManager-ref="transactionManager"
    />
    ...

RollBack - 실패시 packaging이 진행되도록 Rollback

EmpDaoTest

    ...
    @Before
    public void setUp() throws Exception {
        empDao=ac.getBean(EmpDao.class);
        //EmpVo [empno=4, pay=4444, ename=tester4];
        target=EmpVo.Builder()
                    .empno(4)
                    .pay(4444)
                    .ename("tester4")
                    .build();

        PlatformTransactionManager manager=ac.getBean(PlatformTransactionManager.class);
        manager.getTransaction(ac.getBean(TransactionDefinition.class)).setRollbackOnly();
    }
    ...

Maven web 프로젝트 만들기

pom.xml 라이브러리 업데이트 -[web, webmvc, servlet, jsp]

    ...
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
        <scope>provided</scope>
    </dependency>
    ...

maven clean

-[Run As] > [Maven Build]

convert to maven project

- [configure] > [convert to Maven Project]

make web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                       http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

</web-app>

Spring 프로젝트 만들기

Servlet Mapping

web.xml

    ...
    <servlet>
        <servlet-name>day45</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/servlet/day45-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>day45</servlet-name>
        <url-pattern>*.do</url-pattern>    
    </servlet-mapping>
    ...

Controller

day45-servlet.xml

--

applicationContext.xml

    ...
    <bean 
        class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"
    />

ListController

package com.gimhae.controller;

public class ListController implements Controller {
    EmpDao empDao;

    @Autowired
    public void setEmpDao(EmpDao empDao) {
        this.empDao = empDao;
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return new ModelAndView("emp/list","list",empDao.pullList());
    }
}

InsertController

--

DeleteController

--

Pages

/index.jsp

...
<script type="text/javascript">
location.href="index.do"
</script>
...

views/template/head.jspf

...
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="root" value="${pageContext.request.contextPath}"></c:set>
<link rel="stylesheet" type="text/css" href="${root }/css/bootstrap.min.css">
<script type="text/javascript" src="${root }/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="${root }/js/bootstrap.min.js"></script>
<style type="text/css">
body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}
</style>

views/template/menu.jspf

...
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>

 <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li><a href="${root }/">home</a></li>
            <li><a href="${root }/intro.do">intro</a></li>
            <li><a href="${root }/emp/list.do">emp</a></li>
            <li><a href="${root }/login/login.do">login</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>
<div class="container">
      <div class="starter-template">
        <div class="row">
            <div class="col-md-12">

views/template/footer.jspf

...
</div>
</div>
        <div class="col-md-12">
            <address>
                <p>김해캠퍼스 (50834) 경남 김해시 인제로 197</p>
                <p>Copyright &copy;2024 INJE University. All rights reserved.</p>
            </address>
        </div>
    </div>
</div></html>

views/main.jsp

...

<%@include file="template/head.jspf" %>
...
<%@include file="template/menu.jspf" %>

            <div class="jumbotron">
                <h2>환영합니다.</h2>
            </div>

<%@include file="template/footer.jspf" %>
...

views/intro.jsp

...
<%@include file="template/head.jspf" %>
...
<%@include file="template/menu.jspf" %>

            <img src="https://www.inje.ac.kr/kor/assets/images/sub/gimhae-campus-1.jpg">

<%@include file="template/footer.jspf" %>
...

views/emp/list.jsp

...
<%@include file="../template/head.jspf" %>
...
<%@include file="../template/menu.jspf" %>

    <h2 class="page-header">목록</h2>
    <table class="table">
         <thead>
            <tr>
                <td>사번</td>
                <td>이름</td>
                <td>금액</td>
                <td>날짜</td>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="${list }" var="bean">
            <c:url value="detail.do" var="detail">
                <c:param name="idx" value="${bean.empno }"/>
            </c:url>
                <tr>
                    <td><a href="${detail }">${bean.empno }</a></td>
                    <td><a href="${detail }">${bean.ename }</a></td>
                    <td><a href="${detail }">${bean.pay }</a></td>
                    <td><a href="${detail }">${bean.hiredate }</a></td>
                </tr>
            </c:forEach>
        </tbody>
    </table>

<%@include file="../template/footer.jspf" %>
...

views/emp/add.jsp

--

DB 정보 property로 따로 보관하기

applicationContext.xml

	...
	<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
		p:location="classpath:/db.properties"
	/>

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource"
		p:driverClassName="com.mysql.cj.jdbc.Driver"
		p:url="${db.mysql.url}"
		p:username="${db.mysql.user}" p:password="${db.mysql.password}"
	/>
    ...

db.properties

db.mysql.url=jdbc:mysql://localhost:3306/xe
db.mysql.user=scott
db.mysql.password=tiger