Spring 에서 @PathVariable 사용하여 값을 넘겨받을때 값에 . 가 포함되어 있으면 .포함하여 그뒤가 잘려서 들어온다.



@RequestMapping(value = "/user/email/{email}", method=RequestMethod.GET)


위와같은 형식일때 아래와 같이 바꿔주면 제대로 들어온다.



@RequestMapping(value = "user/email/{email:.+}", method = RequestMethod.GET)
public ModelAndView getUserByEmail(@PathVariable("email") String email) {


http://stackoverflow.com/questions/16332092/spring-mvc-pathvariable-with-dot-is-getting-truncated



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



@Aspect annotation 기반 AOP 를 구현해서, 

1. 로그인 하지 않은 사용자가, 로그인페이지를 제외한 다른페이지에 접근했을 때나,

2. 접근권한이 없는(세션값) 사용자가, 접근할 수 없는 페이지에 접근했을 경우 등

  로깅이나, 공통기능을 사이트 전반에 적용시켜야 할 때 이용한다.

@Aspect 어노테이션은 AspectJ 5버전에 새롭게 추가된 어노테이션으로서, xml파일에 Advice 및 Pointcut 등의 설정을 하지않고도,

자동으로 Advice를 적용할 수 있다.


먼저 필요한 lib - 

aspectjweaver-1.6.5.jar


일단은  servlet xml 설정

<aop:aspectj-autoproxy />

<bean id="loginAspect" class="com.common.LoginAspect"/>


다음은 advice와 pointcut을 구현한 Aspect 클래스

@Aspect
public class LoginAspect {

	@Pointcut("execution(public * com.admin..*(..))")
	private void adminMethod(){}
	
	@Pointcut("execution(public * com.bbs..*(..))")
	private void bbsMethod(){}
	
	@Pointcut("execution(public * com.common..*(..))")
	private void commonMethod(){}
	
	@Pointcut("execution(public * com.onm.administrator..*(..))")
	private void onmAdministratorMethod(){}
	
	@Pointcut("execution(public * com.onm.membership..*(..))")
	private void onmMembershipMethod(){}
	
	@Pointcut("execution(public * com.onm.report..*(..))")
	private void onmReportMethod(){}
		
	private String[] access_url = {
			};
	
	@Around(value ="adminMethod() || bbsMethod()  || commonMethod()
                                || onmAdministratorMethod() || onmMembershipMethod() || onmReportMethod()")
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println("#### LoginAspect 시작 ####");		
		HttpServletRequest request = null;
		HttpServletResponse response = null;
		for ( Object o : joinPoint.getArgs() ){ 
			if ( o instanceof HttpServletRequest ) {
				request = (HttpServletRequest)o;
			} 
			if ( o instanceof HttpServletResponse ) {
				response = (HttpServletResponse)o;
			} 
		}
		try{
			HttpSession session = request.getSession();

				String loginId = (String) session.getAttribute("loginId");
				String userEnterType = (String) session
						.getAttribute("UserEnterType");

				System.out.println("### Margo ==> loginId : " + loginId);
				if (loginId == null || "".equals(loginId)) {
					System.out.println("### Margo ==> in if loginId : "
							+ loginId);
					throw new RuntimeException("먼저 로그인을 하셔야 합니다.");
				}			
		}catch(Exception e){
			
			throw new RuntimeException("먼저 로그인을 하셔야 합니다.");

		}		
		Object result = joinPoint.proceed();
System.out.println("#### LoginAspect 끝 ####");		
		return result;
	}
}


1라인 에 적용한 @Aspect 어노테이션을 적용한 클래스는 advice구현메소드나, pointcut정의를 포함할 수 있게된다.


4 라인에 AspectJ의 pointcut 표현식으로 pointcut을 정의한다. 

execution(수식어패턴 리턴타입 클래스이름패턴 이름패턴(파라미터패턴))

수식어패턴: public, protected 등

각패턴은 * 를 이용하여 모든 값을 표현할 수 있고. '..' 를 이용하여 0개 이상이라는 의미를 표현가능

Controller라고 끝나는 이름의 클래스에만 적용할때는 - execution(public * com.bbs..*Controller.*(..))


5라인 처럼 pointcut 정의 메소드는 무조건 리턴타입이 void여야한다.

   이 메소드는 advice 관련 어노테이션인 @around의 값으로 사용될 메소드이다.


이제 trace메서드를 이용해서, 필요한 처리를 구현하면된다.

!!!!!!!!!!!!!!!! 여기서...중요한게 있다!!!!!!

27라인에서 joinPoint는 수행될 메소드인데, 보통 세션값 이용할때 request에서 빼내오는데

그 원래 해당메소드 파라메타로 HttpServletRequest가 지정되어 있지 않으면, request가 null이 되겠지..


끝으로 51,55라인에서 뿌린 Exception을 잡을 놈도 xml에 정의해야한다.

servlet xml에다가 추가해주자

<bean

class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<property name="exceptionMappings">

<props>

<prop key="java.lang.Exception">

index

</prop>

</property>

</bean>

끝~~



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST




nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appProcess' defined in ServletContext resource
[/WEB-INF/Appdi-servlet.xml]: Cannot resolve reference to bean 'SimpleJdbcTemplate' while setting bean property 'template';

nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'SimpleJdbcTemplate' defined in ServletContext resource

[/WEB-INF/Appdi-servlet.xml]: Cannot resolve reference to bean 'DataSource' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DataSource' defined in ServletContext resource

[/WEB-INF/Appdi-servlet.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/KeyedObjectPoolFactory

 
Spring 에서 mysql 로 JDBC 구현할 때, 저딴 에러가 나면 그냥 파일하나 lib 에 넣어주면된다...ㅠㅠ;

ㅅㅂ...내 2시간......

 

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. Cheap Moncler vests for women 2015.01.01 16:07  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  2. New Era 59FIFTY Hats Wholesale Free shipping 2015.01.09 16:46  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  3. Wholesale New Era Caps,Cheap Snapback Hats/Caps 2015.01.09 16:49  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  4. louboutin homme 2015.01.17 10:48  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  5. louboutin pas cher 2015.01.20 04:23  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  6. giubbotti peuterey 2015 2015.01.20 06:35  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  7. hogan scarpe 2015.01.20 08:42  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.

  8. anello tiffany 2015.01.21 07:19  댓글주소  수정/삭제  댓글쓰기

    이용약관위배로 관리자 삭제된 댓글입니다.





Spring 에서 DAO 처리를 위해서 DataSource 를 만들때 에러가 나는 이유

commons-dbcp.jar 파일이 없어서 그런거다. 

그냥 이 파일을 다운받아서 프로젝트 lib 폴더나, 아파치 lib 폴더에 넣어주고,

프로젝트 클린 한번 해주면 에러가 없어진다.

이걸 몰라서 1시간을....ㅠㅠ  아래에 파일링크




http://apache.tt.co.kr//commons/dbcp/binaries/commons-dbcp-1.4-bin.zip
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



구현하는 자세한 설명은 전편에 있음 ( http://winmargo.tistory.com/117 )

이번 글에서는 전편에 다못한 Statement 처리를 위한 다양한 메소드들의 사용법을 살펴보자.

메소드에 쿼리문에 값을 전달하는 방법에는  인덱스 방식과, 네임드 방식이 있다

인덱스는 전편에서도 했고, 쉽기때문에 넘어가고

여기서는 네임드 방식을 쓰겠다.

네임드 방식은 인덱스에서 쿼리에 ? 를 넣은것과같이, ':변수명 또는 :맵의키값' 형식으로 넣는다 .
':' 콜론을 붙이면 되는것이다.

NamedParameterJdbcTemplate 에는 이름 기반의 파라미터를 설정할 수 있도록 해준다.

그 방식에는 두가지 방식이 있는데,

1. Map 을 이용한 파라미터 값 설정

말 그대로 맵을 이용한다. 쿼리문내에 ':' 콜론을 이용해서 키값과 같이 써주고
맵을 인자로 넣어주면, 매핑된다.


2. SqlParameterSource 를 이용한 파라미터 값 설정( 실제는 BeanPropertySqlParameterSource 클래스)

SqlParameterSource 는 인터페이스이기 때문에 실제로 사용할때는 구현한 클래스를 사용한다.

그 클래스가 바로 BeanPropertySqlParameterSource  이다.

생성할 때 인자로 자바빈 클래스를 넣어주면, 자동으로 Bean 클래스 변수와 쿼리안의 : 콜론과 붙여쓰는 이름을

같게 해주면 자동으로 매핑된다.

(MapSqlParameterSource 클래스도 있긴한데 쓸일이 없을것같다.. 불편하다)




이제부터 볼 예제들은  SimpleJdbcTemplate 을 이용한 것이다.



아래 코드는 Map 방식과 BeanPropertySqlParameterSource가 다들어있는 아주 중요한 코드다.
눈 크게 뜨고 살펴보자!!
'
public int setReply(ItemBean itemBean, int originalNumber, int depth){
		
		
		int state = 0;
		
		int firstNumber = originalNumber - 1;
		
		itemBean.setArticle_num(firstNumber);
		
		int lastNumber = firstNumber - (firstNumber % 1000) + 1;
		
		paramSource = new BeanPropertySqlParameterSource(itemBean);
		
		Map map = new HashMap();
		
		map.put("first", firstNumber);
		
		map.put("end", lastNumber);
		
		
		//  - map 방식
		sql = "UPDATE margo SET article_num = article_num - 1 WHERE article_num BETWEEN :end AND :first";
		
		template.update( sql, map);	
		
	
				
		//  - sqlParameterSource 방식
		sql = "INSERT INTO margo " + 
			  "VALUES(:article_num, :id, :title, :body, :password, :count, :depth, sysdate, :fileName)";
		
		return template.update( sql, paramSource);
		
	} // setReply() end;
	
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

23라인을 보면, 쿼리에 값을 주입할때, (BETWEEN :end AND :first) 라고 되어있다

바로 맵으로 end 와, first 키를 생성해 놓은것을, 키값을 콜론과 같이 쓰면

맵에서 자동으로 뽑아 쓴다.


그 다음은 31라인은 BeanPropertySqlParameterSource() 메소드를 이용해서 값을 주입하는 방식이다.

 13라인에서 paramSource에 생성자로 메소드 인자로 넘겨받은 자바빈 파일을 넣어주면,

자동으로 자바빈안의 변수와 값을 매핑 시켜준다.

위 두가지 방식인 네임드(이름기반) 파라미터방식은 여러모로 쓸모가 많을것 같다. 잘 배워두자~ 

 




아래는 간단한 컬럼하나 가지고오는 예제
_
	// 총글의 개수를 가지고온다. 안에 사용한 두 가지 코드는 결과는 동일하다.
	public int getArticleCount() {
	
		sql = "SELECT COUNT(*) FROM margo";
		
		
		// Object 로 리턴값을 명시해주고 컬럼 하나를 받아온다.
		totNum = template.queryForObject( sql, Integer.class);
		 

		// queryForInt 메소드를 써서 int값 만 받아올때 쓸 수 있다.
		totNum = template.queryForInt(sql);
		
		return totNum;
		
	}
	
 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

코드만 봐도 대충 알수있을 만큼 간단하다.
어떤 객체 하나를 받아올때는 QeuryForObject 를 쓰고, 리턴타입을 지정해주려면,
9라인의 방식을 쓴다. 여기서는 int 지만, 아래 예제에서 클래스를 쓰는법을 설명하겠다.
정수만 받을때는 13라인을 쓰면된다. 






QueryForObject 로 클래스(자바빈)를 리턴받는 법
<T> T
queryForObject(String sql, RowMapper<T> rm, Object... args) 
.
	public ItemBean getContent(final int contentNum) {
		
		sql = "SELECT * FROM margo WHERE article_num=?";
			
		// 익명클래스 이용한 방법.
		return template.queryForObject( sql, 
									
			new RowMapper(){
					
					@Override
					public ItemBean mapRow(ResultSet rs, int rowNum) throws SQLException{
						
						ItemBean itemBean = new ItemBean();
						
						itemBean.setArticle_num(rs.getInt("article_num"));
						itemBean.setId(rs.getString("id"));
						itemBean.setTitle(rs.getString("title"));
						itemBean.setBody(rs.getString("body"));
						itemBean.setCount(rs.getInt("hit"));
						itemBean.setDepth(rs.getInt("depth"));
						itemBean.setPassword(rs.getString("password"));
						itemBean.setWrite_date(rs.getTimestamp("write_date"));
						itemBean.setFileName(rs.getString("filename"));
					
						return itemBean;
					
					}// mapRow() end;
					
			}, // RowMapper 클래스 여기까지임.
			
			new Object[]{ contentNum });
		
	}
 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

익명클래스 방식을 이용해서 리턴 클래스를 지정해주고, ResultSet 을 이용해서 리턴받는 방법이다.

RowMapper클래스는 mapRow() 메소드를 오버라이드 해야된다.
(35라인은 무시, 코드하이라이트 버그같은데, 제네릭을 태그로인식해서 지맘대로 닫는코드를 생성...짜증난다 ㅡㅡ;) 






아래 코드는 UPDATE 메소드를 이용해서 DB에 자료를 Insert 하는 쓰는 법이다.
(SQL쿼리 update와 헷갈리면 안된다. 쓰는건 전부 update() 메소드다)
int update(String sql, Object... args)
public int setContent(ItemBean itemBean){
		
		int state = 0;
		
		String sql = "INSERT INTO margo VALUES(margo_sequence.NEXTVAL,?,?,?,?,?,?,sysdate, ?)";
		
		
		state = template.update( sql, 
							new Object[]{ itemBean.getId(),
											itemBean.getTitle(),
											itemBean.getBody(),
											itemBean.getPassword(),
											itemBean.getCount(),
											itemBean.getDepth(),
											itemBean.getFileName()});
		
		return state;
	}
	

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

update() 메소드가 나왔는데, 
쿼리에 값 주입방식은 인덱스 기반 파라미터 방식으로 집어넣고 있다.
뭐...간단하다  .


YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST




SimpleJdbcTemplate 클래스는

JdbcTemplate 과 NamedParameterJdbcTemplate 을 합쳐 놓은 템플릿 클래스로서

이름 기반의 파라미터 설정인덱스 기반의 파라미터 설정을 모두 지원한다.

이름 기반의 파라미터를 설정할 때에는 Map과 SqlParameterSource 두 가지 방법을 모두 사용할 수 있다.

SimpleJdbcTemplate 클래스가 제공하는 주요메소드는 아래와 같다. 



Method Summary

<T> List<T> 
query(String sql, RowMapper<T> rm, Map<String,?> args)
<T> List<T>
query(String sql, RowMapper<T> rm, Object... args) 
<T> List<T>
query(String sql, RowMapper<T> rm, SqlParameterSource args) 




int queryForInt(String sql, Object... args) 
int queryForInt(String sql, SqlParameterSource args) 



List<Map<String,Object>> queryForList(String sql, Object... args)
List<Map<String,Object>> queryForList(String sql, SqlParameterSource args) 



<T> T
queryForObject(String sql, RowMapper<T> rm, Object... args) 
<T> T
queryForObject(String sql, RowMapper<T> rm, SqlParameterSource args)  



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



이렇게 받은 template을 처리해야될 방법에 따라 잘 만들어진 메소드를 고르기만 하면 된다.

가장 기본인 DB 에서 모든 글을 긁어온다음 list 를 만들어서 보여주는 걸 해보자.
나는  List query(String sql, RowMapper<T> rm, Object... args) 이놈을 골랐다!

그럼이제 리스트 를 보여주기위한 작업을 해보자.


1.  servlet.xml 에 SimpleJdbcTemplate 을 등록한다. (http://winmargo.tistory.com/116 참고)


2. 처리를 수행하는 구현클래스에서 SimpleJdbcTemplate 을 이용해서 Statement 작업을 구현하면 끝이다.
  


구현클래스를 보자
     private SimpleJdbcTemplate template = null;
	
   
	
	
	//모든글을 몽땅 가져온다.
	@Override
	public List getArticles(int index) {
		
						
		sql =  "SELECT * FROM (SELECT ROWNUM rnum,A.* FROM " +
				"(SELECT * FROM margo ORDER BY article_num DESC) A ) "
				+ "WHERE rnum BETWEEN ? AND ?";
	
					
			
		return template.query( sql, 
				
				   				   new ItemBeanRowMapper(),
								   new Object[]{ index, (index + 10)});
		
	}
	
 

코드를 보면 17 라인에 return 값에 simpleJdbcTemplate을 줬다. 리턴값이 List


첫 번째 인자에 sql쿼리를 넣고,

두번째에 3라인에 선언한 ItemBeanRowMapper() 를 넣어주고 있다.(아래에서 자세히)

세 번째 인자에는 쿼리문 안에 있는 '?' 에 대응되어 들어갈 값 2개가 있다. ? 순서와 개수에 맞게 넣어주면 된다.(이것이 인덱스 방식이다)
  
  - 쿼리에 값을 넣는 방법은 두가지가 있는데, 인덱스 파라미터 방식과, 네임드 파라미터(이름 기반) 방식이 있다.
     
     ? 에 순서대로 대입하는 방식은 Index 기반 파라미터 방식이고,
     
      넣어줄 값을 들고있는 맵이나, 빈클래스의 변수을 ':' 콜론과 같이 쓰면 값이 들어가는데
     이 방식이 이름 기반의 파라미터 방식이다.  




기본적인 동작 방식은 template 이 sql쿼리문에 값을 넣고, Statement 처리를 한후

19라인에 있는 ItemBeanRowMapper()  클래스를 이용해서 ResultSet 을 처리한후

리턴되어오는 어떤 클래스(여기서는 Bean파일이다) 를  List 에 자동으로 담은후

그 List 를 리턴해준다.


여기서 중요한건 19라인에 ItemBeanRowMapper()  클래스 이다.

이 클래스는 위설명과 같이 쿼리수행후 리턴된 다수개의 레코드들을 모은 ResultSet에서

레코드컬럼과, 자바빈 변수를 Mapping 시켜

한 개의 레코드를 하나의 자바빈 으로 만들어서 return 시켜주면

Template 이 자동으로 List 에 담아서 Template 수행후 마지막으로 리턴 시킨다.

ItemBeanRowMapper() 클래스는 RowMapper<T> 를 구현해야한다.


ItemBeanRowMapper 클래스를 보자


package Spring;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class ItemBeanRowMapper implements RowMapper {

	
	
	@Override
	public ItemBean mapRow(ResultSet rs, int rowNum) throws SQLException {
		
		ItemBean itemBean = new ItemBean();
		
		String str ="";
				
		itemBean.setArticle_num(rs.getInt("article_num"));
		itemBean.setId(rs.getString("id"));
		itemBean.setTitle(rs.getString("title"));
		itemBean.setCount(rs.getInt("hit"));
		itemBean.setDepth(rs.getInt("depth"));
		itemBean.setWrite_date(rs.getTimestamp("write_date"));
		itemBean.setFileName(rs.getString("filename"));
		
		int cnt = itemBean.getDepth();
		
		while( cnt > 0){
			
			str += "    ";
			cnt--;
		}

		itemBean.setSpace(str);
		
		return itemBean;
	}

}

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
메소드 코드를 대충보면 딱 알수 있을만큼 간단하다.  

mapRow(ResultSet rs, int rowNum)

메소드를 오버라이딩 하면서 인자로  ResultSet 과 정수하나를 주는데

뒤에정수는 레코드 개수이다.

사용할 일이 있을때 사용하면된다.

빈파일에 컬럼을 매핑시킨후, 빈파일을 리턴하면

template 이 자동으로 List 에 쑥쑥 넣는다.


수가지의 방식이 있는데, 일단은 제일 요긴하고 잘쓰일 방법으로 구현해보았다.


다음편에는 다른 여러가지 구현방식을 알아보겠음



YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



DAO Statement 처리를 위해서 Spring에서 JDBC 를 지원하는데 

JdbcTemplate, namedParameterJdbcTemplate, SimpleJdbcTemplate 세가지 방식이 있다.

Simple 방식이 앞의 2가지 방식을 모두사용 할 수 있는 방식이다.

메소드 인자 위치가 조금 다르기때문에, 확인후 사용해야한다.





JdbcTemplate 을 사용하려면, 일단 DataSource 를 넣어준후 생성해야된다.


여러가지가 있겠지만, 일단 Template 에는 DataSource 가 생성자로 넣어주는 방식이 편한데


 JdbcTemplate 클래스에는 setDataSource() 메소드가 있어서, 프로퍼티 방식으로 

생성을 해도 JdbcTemplate 이 문제없이 생성되지만,


SimpleJdbcTemplate 클래스는 setDataSource() 메소드가 없기 때문에

생성자 방식으로만 생성 할 수 있다. 




↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

코드를 보면, 일단 DataSource를 만들어주고,

Template 을 두가지 방식으로 선언했다.

p:dataSource-ref 가 프로퍼티 방식으로 클래스를 주입하는 것이다.

JdbcTemplate 은 프로퍼티 방식으로 setter 를 이용해서 DataSource를 넣어준후 클래스를 생성하고

SimpleJdbcTemplate 은 setter가 마련이 안되있기 때문에, 생성자 방식으로만 클래스 생성이 가능하다.


이제 그냥 자바 클래스에서 Template 선언후, 생성자로 받던지, 프로퍼티로 받아서 
사용하면된다. 
 





YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


1. SELECT 쿼리 예제
// 숫자형을 리턴할때
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
"select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

// 도메인 객체와 매핑할때
public Collection findAllActors() {
return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
}
}

2. INSERT, UPDATE, DELETE 쿼리 예제
this.jdbcTemplate.update(
"insert into t_actor (first_name, surname) values (?, ?)",
new Object[] {"Leonor", "Watling"});

SQL문만 다를뿐 모두 update 메서드를 사용하면 된다.
또한 간단한 스토어드프로시져도 update메서드를 이용할수 있다.
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
new Object[]{Long.valueOf(unionId)});


3.기타
그밖에 DDL 같은 특수한 쿼리를 실행할때는 execute()메서드를 사용하기도 한다.
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");


출처 : 
http://yunsunghan.tistory.com/239
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



스프링에서 지원하는 다운로드를 쓰려면 아래와 같이 하시오.!

일단 기본적으로 파일 다운로드 처리하기에 앞서서 알아야 할 사항을 배워보도록 하자.

1. 파일링크를 클릭할때 컨트롤러 클래스에게 파일패스와 파일이름을 던져주고 

2. 받은 컨트롤러 클래스에서 그 파일 패스와 파일이름으로 file 을 만들어서 (DownloadController)

3. 뷰로 전달을 할 것이다. 

4. 그럼 뷰에서 받은 file 정보를 이용해서 실제 파일을 읽어들인 다음 원하는 위치에 쓰는 작업을 한다. (DownloadView)

 

 

일반적인 컨트롤러 클래스에서 작업을 한 후, 뷰 페이지로 결과값을 뿌려주는 것인데

일반적인 뷰페이지는 JSP 페이지였다.

하지만 다운로드에 사용될 뷰는 JSP 가 아니라  클래스 파일이다. 



그렇기 때문에 아래처럼 일반적으로 사용하던 viewResolver 가 처리하는 것이 아니라

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/"/>

<property name="suffix" value=".jsp"/>

<property name="order" value="1"/>

</bean>  






download 만을 처리하는 viewResolver 가 따로 존재해야 한다. 여기에는 id값이 없다...주의할것!!!!!!!!!

<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">

      <property name="order" value="0"/>

</bean>


주의할 점은 위 두 코드에 포함된 프로퍼티를 보면 order 라는 프로퍼티가 있는데

이 프로퍼티는 두개이상 존재하는 viewResolver 를 위해서

우선순위를 매기는 것이다. 만약 우선순위를 명시 하지 않으면 "가장 낮은 우선순위를 갖게 된다."

우선순위는 "0"이 가장 먼저 실행되고, 이후로 매겨지는 순서에 따라 실행된다.


다음 viewResolver가 실행되는 기준은 "null" 이 반환되느냐 가 기준이다. 

그렇기 때문에 널값이 있을수 없는 InternalResourceViewResolver 가 우선순위가 높게 되면,
다른 viewResolver 는 사용되지 않게되는
문제가 있다. (항상 뷰 이름에 매핑이 되는 뷰 객체를 리턴하기 때문)

 그래서 InternalResourceViewResolver 은 우선순위가 가장 낮아야 한다.





그러면 이제 BeanNameViewResolver 를 사용하는 법을 알아 보자


BeanNameViewResolver (파일 다운로드 viewResolver)

"null" 이 반환되지 않는다면, (즉 컨트롤러 클래스에서 리턴되온 뷰페이지 값과 일치하는 빈이 있는 경우)


컨트롤러 클래스에서 리턴되온 뷰페이지 값과 일치하는  빈이 등록되있는 경우는 그 빈에 해당하는 컨트롤러 클래스가

파일 다운로드를 처리하게 된다.  

그렇기 때문에 컨트롤러 클래스에서 viewResolver 로 던져줄 뷰페이지 이름과, 처리할 View 클래스 빈이름이 같아야 한다. 
(이말을 반대로 하자면, 실제 jsp 가 보여져야될 때는 리턴값과, view 빈 이름이 같아서는 절대 안된다.)
 

<bean id="download" class="Spring.DownloadView"/>
    - 이 코드가 다운로드를 처리할 뷰 클래스를 등록하는 것이다.
       저기 id="download" 라고 되있는 부분과, 클래스에서 리턴된 값이 같아야 한다.



그리고 url 을 처리할 컨트롤러 클래스도 등록되야되겠지.

<bean id="down" class="Spring.DownloadController"/>




여기 까지가 좀 복잡하지만 servlet.xml 파일을 설정하는 부분이다. 
하나하나 천천히 다시 읽어보면 이해가 될 것이다.






이제 처음부터 하나씩 따라가보자.


파일 이름에 링크를 걸어서 컨트롤러 클래스로 넘기는 부분부터 시작

<a href="/Spring_margo/download.do?path=${path }&fileName=${itemBean.fileName }" >       ${itemBean.fileName }      </a>

download.do 로 파일네임과 패스를 넘기게 되어있다. 







그럼 저 url 을 처리하는 컨트롤 클래스는 아래와 같다.
<bean id="down" class="Spring.DownloadController"/>
@Controller
public class DownloadController implements ApplicationContextAware{

	private WebApplicationContext context = null;
	
	@RequestMapping("download.do")
	public ModelAndView download(@RequestParam("path")String path, 
                                  @RequestParam("fileName")String fileName){
		
		String fullPath = path + "\\" + fileName;
		
		File file = new File(fullPath);
		
		return new ModelAndView("download", "downloadFile", file);
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		// TODO Auto-generated method stub
		
		this.context = (WebApplicationContext)arg0;
		
	}
	
}

@RequestMapping("download.do") 어노테이션으로 지정해준것처럼
download.do 가 들어오면 저 메소드가 동작한다.
링크에서 준것처럼 패스와 파일네임을 받아서

파일에 조합해서 쓰고 "download" 뷰페이지로 파일을 "downloadFile"이름으로 삽입하고 리턴시킨다.

그러면 <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">

저 viewResolver 가 먼저 리턴을 처리하려 할 것이다.

"download"로 등록된 빈이 있는지 찾아 보는데 우리는 아까 "download"로 뷰클래스를 등록시켜 놓았다. 
<bean id="download" class="Spring.DownloadView"/>

이제 DownloadView.java 클래스가 뷰페이지로 동작할 것이다.
뷰페이지에서는 map 에 등록된 파일을 이용해서 encoding 설정과 헤더설정을 해준 후 파일을 지정위치에 쓴다.

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class DownloadView extends AbstractView {


	public void Download(){
		
		setContentType("application/download; utf-8");
		
	}
		
	@Override
	protected void renderMergedOutputModel(Map model,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		
		File file = (File)model.get("downloadFile");
		System.out.println("DownloadView --> file.getPath() : " + file.getPath());
		System.out.println("DownloadView --> file.getName() : " + file.getName());
		
		response.setContentType(getContentType());
		response.setContentLength((int)file.length());
		
		String userAgent = request.getHeader("User-Agent");
		
		boolean ie = userAgent.indexOf("MSIE") > -1;
		
		String fileName = null;
		
		if(ie){
			
			fileName = URLEncoder.encode(file.getName(), "utf-8");
						
		} else {
			
			fileName = new String(file.getName().getBytes("utf-8"));
			
		}// end if;

		
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
		
		response.setHeader("Content-Transfer-Encoding", "binary");
		
		OutputStream out = response.getOutputStream();
		
		FileInputStream fis = null;
		
		try {
			
			fis = new FileInputStream(file);
			
			FileCopyUtils.copy(fis, out);
			
			
		} catch(Exception e){
			
			e.printStackTrace();
			
		}finally{
			
			if(fis != null){
				
				try{
					fis.close();
				}catch(Exception e){}
			}
			
		}// try end;
		
		out.flush();
		
	}// render() end;
}


여기까지가 

파일링크를 클릭해서 다운로드를 하는 일련의 과정을 코드로 풀어논 것이다.

복잡해 보이지만, ....복잡하다.......죄송;




열심히 뚝딱뚝닥 거리다보면 위그림처럼 다운로드가 될것이다!!!!!!!!!


YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 로스비 2011.08.17 20:05 신고  댓글주소  수정/삭제  댓글쓰기

    저번 처럼 이상하게 건들지도 않은 ListImpl이 오류가 생깁니다.

  2. 메로메로 2012.10.03 18:46 신고  댓글주소  수정/삭제  댓글쓰기

    지나가다가 잘배우고 갑니다. 많은 도움이 되었습니다. 이제 완전 초보인데요.

    이클립스를 세팅중인데요, 보리마고님의 이클립스를 보면 검은바탕까진 저도 설정을 햇는데, 나머지 글씨를 파란색과 형광색으로 하고 싶은데 방법좀 알려주시면 정말 감사하게습니다. ㅠ

    • 보리마고 2012.10.08 17:53 신고  댓글주소  수정/삭제

      이클립스 세팅이 아니라, 티스토리에 포스팅할때 하이라이팅 기능을 써서 코드를 저렇게 보이게 한겁니다. 이클립스는 코드 하이라이트 플러그인이 있으니 여러가지 찾아보셔서 마음에 드시는걸로 하세요 ㅎㅎ

  3. eugene 2014.05.07 12:19 신고  댓글주소  수정/삭제  댓글쓰기

    1.png가 나오는 이유는 무엇인지 궁금합니다
    직접 뷰로 값을 넘겨주는 건가요? 예제대로 하고 있는데 파일네임에 계속 null이 잡히네요

  4. kim 2014.05.30 11:31 신고  댓글주소  수정/삭제  댓글쓰기

    혹시 위 소스를 war 파일로 좀 받을수있나요?

  5. kim 2014.05.30 11:32 신고  댓글주소  수정/삭제  댓글쓰기

    가능하시다면 hg97804@naver.com
    으로 부탁좀 드리겟습니다

  6. Nathan 2015.01.16 10:30 신고  댓글주소  수정/삭제  댓글쓰기

    큰도움이 되었습니다. 감사합니다!

  7. LHN 2015.07.08 16:21 신고  댓글주소  수정/삭제  댓글쓰기

    정말 잘 배워 가요 ㅠ-ㅠ! 감사합니다.

  8. 오늘은진돗개 2015.11.25 15:00 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    궁금한게 있는데요.

    download.do에서 파일정보 데이터를 쿼리로 가져와서 다운받게하려고 하는데 파일 안만들고 가능한가요?
    File file = new File(fullPath);
    return new ModelAndView("download", "downloadFile", file);

    이거 대신 return new ModelAndView("download", "downloadFile", binary); 이런식으로요




가장 기본적으로 파일 업로드를 하려면 

html 문서 안에 <form> 태그에서

<form method="post" enctype="multipart/form-data">
...........
</form> 







Multipart 지원기능을 사용하려면  먼저 multipartResolver 를 스프링 설정 파일에 등록 해주어야 한다.

CommonsMultipartResolver 를 multipartResolver 로 사용하려면, 다음과 같이 

빈 이름을 반드시!!!  "multipartResolver" 로 등록해서 사용하면 된다.
다른이름으로 등록하면 안된다. 그냥 안된다. 절대안된다. 업로드를 포기하라.

<bean id="multipartResolver
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

참고로 프로퍼티들은

maxUploadSize(최대업로드 가능한 바이트크기)
maxInMemorySize(디스크에 임시 파일을 생성하기 전에 메모리에 보관할수있는 최대 바이트 크기)
defaultEncoding(요청을 파싱할 때 사용할 캐릭터 인코딩. 기본값 ISO-8859-1)





이제 컨트롤러 파일에서 업로드가 되는 파일에 접근을 해야하는데, 

빈객체를 이용해서 받는 방법(커맨드 객체), 
@RequestParam 어노테이션을 이용하는 방법,
MultipartHttpServletRequest 를 이용하는 방법,

머 편한 방법을 택하면 된다.

주의 할 점은 <input type="file" name="file" size="50">
폼에서 파일프로퍼티 이름을 "file"로 썼다면, 
업로드될 파일에 접근 프로퍼티도 항상 무조건 네버! "file" 이 되어야 한다.


빈객체를 이용하는 방법은
public class ItemBean {

	MultipartFile file;
	

	public MultipartFile getFile() {
		return file;
	}

	public void setFile(MultipartFile file) {
		this.file = file;
	}

	
자바빈 파일에 set,get을 등록해주면 자동으로 들어간다. 



다음은 @RequestParam 어노테이션을 이용한 방법
	

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView process(HttpSession session, ItemBean itemBean,
								@RequestParam("file")MultipartFile file) {



나머지는 귀찮아서....

일단 이렇게 하면 MultipartFile 타입으로 file 에 업로드된 파일이 들어오게된다.


여기까지 했으면 다 끝난거다. 이제 쓰기만 하면 된다.

쓰는법은 하고싶은데로 하면되는데, 

MultipartFile 의 주요 메소드는

 String getName() 파라미터 이름을 구한다. 
 String getOriginalFilename()  업로드 한 파일의 실제!! 이름을 구한다.
 boolean isEmpty()  업로드 한 파일이 존재하지 않는 경우 true를 리턴한다.
 long getSize()  업로드한 파일의 크기를 구한다.
 byte[] getBytes() throws IOException  업로드 한 파일 데이터를 구한다. --> 이걸로 파일 쓰면된다.
 InputStream getInputStream()  InputStrem을 구한다.
 void transferTo(File dest)  업로드 한 파일 데이터를 지정한 파일에 저장한다. --> 요고도 파일쓰는거다.




걍 쉽게 MultipartFile.getBytes() 를 이용해서 쓰는게 편하다.
package Spring;

import java.io.FileOutputStream;

import org.springframework.web.multipart.MultipartFile;

public class FileWriter {

	private FileOutputStream fos;
	
	public void writeFile(MultipartFile file, String path, String fileName){
		
		try{
		
			byte fileData[] = file.getBytes();
			
			fos = new FileOutputStream(path + "\\" + fileName);
			
			fos.write(fileData);
		
		}catch(Exception e){
			
			e.printStackTrace();
			
		}finally{
			
			if(fos != null){
				
				try{
					fos.close();
				}catch(Exception e){}
				
				}
		}// try end;
		
	}// wirteFile() end;
}




업로드한 파일을 특정파일로 저장하고 싶다면 MultipartFile.transferTo() 를 쓰면 편하다.

File file = new File(filePath + fileName);

multipartFile.transferTo(file); 





YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 탐구생활 2012.10.16 12:07 신고  댓글주소  수정/삭제  댓글쓰기

    '그냥 안된다. 절대안된다. 업로드를 포기하라.'

    웃겨서 댓글답니다. ㅋㅋㅋ

  2. 휴렛 2014.11.20 10:56 신고  댓글주소  수정/삭제  댓글쓰기

    감사합니다. 덕분에 놓친부분 찾고 갑니다^^

  3. 방문객 2015.01.07 15:25 신고  댓글주소  수정/삭제  댓글쓰기


    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
    설정덕분에 해결하고 지나갑니다 ^^