:어노테이션 통한 JNDI TREE의 자원객체 획득 방법은, 오로지 Servlet Class내에서만 유효함.
일반적인 다른 클래스에서 자원객체를 얻어오려면, JNDI Lookup 을 해야 한다.
<Context>
<!-- url="jdbc:oracle:thin:@localhost:1521/seoul" -->
<!-- cloud가 아닌 local db 사용시 -->
<!-- maxActive deprecared -->
<!-- <Resource
name="jdbc/OracleLocalDB"
auth="Container"
type="javax.sql.DataSource"
username="scott"
password="oracle"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@seoul?TNS_ADMIN=C:/u01/oracle/product/19.3.0/dbhome/network/admin"
maxTotal="8"
maxIdle="2"
loginTimeout="1"
/> -->
<!-- name="jdbc/~~~~" 가 관례임
auth="Container" :인증은 WAS가 대신하겠다.
maxTotal 개수만큼 connection을 만들 수 있음 (connection pool. 개발자가 생성하는것이 아니라 WAS가 생성.)
maxIdle = 2초 이상 아무도 커넥션을 이용하지 않으면 자동으로 줄어듦. == Shrinking
longinTimeout : 로그인이 1초 안에 이루어지지 않으면 예외를 발생시킴.
하나의 커넥션을 가져다 쓰고, 다시 pool에 넣어놓는 재사용.
적은 수의 커넥션 만으로도 대량의 트랜잭션을 처리할 수 있다.-->
<!-- cloud db 에서 driverSpy 사용하지 않을 떄 -->
<!-- maxActive deprecared -->
<Resource
name="jdbc/OracleCloud"
auth="Container"
type="javax.sql.DataSource"
username="SCOTT"
password="Oracle12345!!!"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@db202106301639_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"
maxTotal="8"
maxIdle="2"
loginTimeout="1"
/>
<!-- cloud db에서 driverSpy 사용할 때 -->
<!-- maxActive deprecared -->
<!-- <Resource
name="jdbc/OracleCloudWithLog4jdbc"
auth="Container"
type="javax.sql.DataSource"
username="SCOTT"
password="Oracle12345!!!"
driverClassName="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"
url="jdbc:log4jdbc:oracle:thin:@sdb202106301639_high?TNS_ADMIN=C:/u01/oracle/product/19.3.0/dbhome/network/admin"
maxTotal="8"
maxIdle="2"
loginTimeout="1"
/> -->
</Context>
J2EE의 주요 인터페이스.
-> Was 에 우리가 사용한 DataSource리소스 객체같은 다양한 타입의 객체를
JNDI tree에 올려놓고,
-> JNDI lookup 이라는 행위를 통해, JNDI tree에 올려놓은 다양한 자원객체를 얻는 표준 방법
Was라는 컨테이너가 있고, 이 안에 나무가 있다고 생각하자.
뿌리로부터 줄기가 올라와서 가지를 치고, 사과가 열렸다.
이 사과가 자원객체임.
이렇게 사과(자원객체)를 올려놓는 행위가 곧 Binding. 나뭇가지에 사과를 묶어놓는 것.
바인딩 할 때 각각의 자원객체에는 이름을 붙여준다. (context.xml파일에서 설정)
이 나무를 탐색해서, 내가 원하는 이름속성의 사과를 찾을 때
@Resource어노테이션의 name속성을 가지고 찾아옴. => 이렇게 검색하는 행위가 lookup임.
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@Log4j2
@NoArgsConstructor
@WebServlet("/EmpInsert")
public class EmpInsertServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// @Resource(name="jdbc/OracleCloud")
private DataSource ds;
private String sql = "INSERT INTO emp(empno, ename, sal, deptno) VALUES (?,?,?,?)";
@Override
public void init(ServletConfig config) throws ServletException{
log.debug("init(config) invoked.");
// 2. JNDI lookup을 통해 DataSource 객체를 얻고, Connection Pool을 사용하기
Context ctx = null; //자원객체이므로 반드시 사용이 끝나면 close 해줘야 함.
//단, 이 context 인터페이스는 AutoClosable하지 않으므로 직접 close해주어야 함.
try {
// 2-1. JNDI tree의 뿌리(root)를 먼저 접근
ctx = new InitialContext();
log.info("\t ctx: " + ctx);
// 2-2. JNDI root를 통해 JNDI tree를 looup 해야 함.
// 자원객체의 이름 지정은 표준화 되어있음.
// java:comp/env/자원객체이름
// Object obj = ctx.lookup("java:comp/env/jdbc/OracleCloud");
Object obj = ctx.lookup("java:comp/env/jdbc/OracleCloud");
this.ds = (DataSource)obj;
log.info("\t ds: " + ds);
}catch(Exception e) {
throw new ServletException(e);
}finally {
if(ctx != null) {
try {ctx.close();}
catch(NamingException e) {;;} //try-catch
} //if
} //try-catch-finally
}//init
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
log.debug("service(req,res) invoked.");
try {
// 1. 웹 브라우저에서 전송한 파라미터들을 획득
String empno = req.getParameter("empno");
String ename = req.getParameter("ename");
String sal = req.getParameter("sal");
String deptno = req.getParameter("deptno");
// 2. DataSource(Connection Pool)로부터 Connection 얻기
Connection conn = ds.getConnection(); //자원객체 1
// Binding작업 수행
PreparedStatement pstmt = conn.prepareStatement(sql); //자원객체 2
pstmt.setInt(1, Integer.parseInt(empno));
pstmt.setString(2, ename);
pstmt.setDouble(3, Double.parseDouble(sal));
pstmt.setInt(4, Integer.parseInt(deptno));
int affectedLines = pstmt.executeUpdate();
// 3. 응답문서 준비
res.setContentType("text/html; charset=utf8");
PrintWriter out = res.getWriter(); //자원객체 3
try(conn; pstmt; out;){ //connection을 끊는것이 아니라 connection pool로 반납하는 것!
if(affectedLines>0) {
out.println("저장 성공");
}else {
out.println("저장 실패");
} //if-else
out.flush();
}//try-with-resources
} catch (Exception e) {
throw new ServletException(e);
} //try-catch
} //service(req, res)
} //end class
HikariCP ==> javax.sql.DataSource의 구현객체 제공