https://github.com/antop-dev/example/tree/master/mybatis-rowbounds-example


Mybatis에 RowBounds 클래스를 이용해서 페이징 처리를 할 수 있다.

package org.antop.mybatis.mapper;

import org.antop.mybatis.model.Employee;
import org.apache.ibatis.session.RowBounds;

import java.util.List;

public interface EmployeeMapper {
	List<Employee> select(RowBounds rowBounds);
}

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.antop.mybatis.mapper.EmployeeMapper">
    <resultMap id="BaseResultMap" type="org.antop.mybatis.model.Employee">
        <id property="no" column="emp_no" />
        <result property="gender" column="gender" typeHandler="org.antop.mybatis.handler.GenderTypeHandler" />
        <association property="name" javaType="org.antop.mybatis.model.Name">
            <result property="first" column="first_name" />
            <result property="last" column="last_name" />
        </association>
    </resultMap>

    <select id="select" resultMap="BaseResultMap">
        select
          *
        from
          employees
        order by
          emp_no asc
    </select>
</mapper>

위와 같이 맵퍼와 XML 이 있다.

RowBounds rowBounds = new RowBounds(5, 10);
// 15건을 가져와서 앞에 5건 건너띔
List select = mapper.select(rowBounds);

결과는 10건이 나왔지만 어떻게 동작할까?

여기저기 찾아보고 소스도 대충(?) 보면 offset + limit 만큼 가져와서 offset 만큼 건너띤다.

페이징이 뒤로 갈 수록 느려지게 된다.

RowBounds rowBounds = new RowBounds(29990, 10);
// 30000건을 가져와서 앞에 29990건 건너띔
List select = mapper.select(rowBounds);

데이터의 양이 적다면 쿼리에서 페이징을 하지 않고 RowBouns를 이용하면 빠르게 개발할 수 있을 것이다.

하긴 요즘 수천만건의 데이터를 페이징에서 마지막 페이지를 볼 일이 많을까? -_-/


XML 쿼리문에 페이징을 넣지 않고 RowBounds 클래스를 사용하면 자동으로 페이징 쿼리가 실행되게 할 수 없을까?

방법이 있다! Mybatis Intercepter를 이용하면 된다. (인터셉터의 자세한 사용법은 다루지 않겠다 ㅠㅠ)

  1. Mybatis에서 쿼리를 날리기 전에 가로챈다.
  2. RowBounds가 있으면 쿼리에 페이징 문장를 적용한다.
  3. RowBounds를 제거하여 Mybatis에서 페이징 처리를 하지 않도록 한다.

아래 소스는 쿼리에 MySQL용으로 limit 문을 붙여준다.

package org.antop.mybatis.intercepter;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MysqlRowBoundsInterceptor implements Interceptor {
	private static final Logger logger = LoggerFactory.getLogger(MysqlRowBoundsInterceptor.class);

	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
		String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		RowBounds rb = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");

		logger.debug("originalSql = {}", originalSql);
		logger.debug("RowBounds = {}", rb);

		if (rb == null || rb == RowBounds.DEFAULT) {
			// RowBounds가 없으면 그냥 실행
			return invocation.proceed();
		}

		// RowBounds가 있다!
		// 원래 쿼리에 limit 문을 붙여준다.
		StringBuffer sb = new StringBuffer();
		sb.append(originalSql);
		sb.append(" limit ");
		sb.append(rb.getOffset());
		sb.append(", ");
		sb.append(rb.getLimit());

		logger.debug("sql = {}", sb.toString());

		// RowBounds 정보 제거
		metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
		// 변경된 쿼리로 바꿔치기
		metaStatementHandler.setValue("delegate.boundSql.sql", sb.toString());

		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {

	}

}

SqlSessionFactory 설정하는 부분에 인터셉터를 적용하면 된다.

<?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:mybatis="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <mybatis:scan base-package="org.antop.mybatis"/>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
        <property name="plugins">
            <list>
                <!-- 인터셉터 설정 -->
                <bean class="org.antop.mybatis.intercepter.MysqlRowBoundsInterceptor"/>
            </list>
        </property>
    </bean>

    <bean id="dataSource" ... />
</beans>

실행시켜 보면 쿼리문에 페이징이 적용되서 쿼리가 날라가는 것을 확인할 수 있다.

select * from employees order by emp_no asc limit 29990, 10


출처

http://www.programering.com/a/MTM5gTNwATQ.html

http://hjw1456.tistory.com/10

'Framework > Mybatis' 카테고리의 다른 글

Mybatis Interceptor + RowBounds 를 이용한 페이징 처리  (0) 2017.12.12

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

GWT with Maven

Posted at 2015.03.18 02:31 | Posted in Framework

Installed the Java SDK


If you don’t have a recent version of the Java SDK installed, download and install Sun Java Standard Edition SDK.


적어도 이 문서를 찾아보는 이라면 자바는 이미 깔려 있을 것이라고 굳게 믿는다... -_-/




Installed Eclipse or your favorite Java IDE


In these tutorials, we use Eclipse because it is open source. However, GWT does not tie you to Eclipse. You can use IntelliJ, NetBeans or any Java IDE you prefer. If you use a Java IDE other than Eclipse, the screenshots and some of the specific instructions in the tutorial will be different, but the basic GWT concepts will be the same.


If your Java IDE does not include Apache Ant support, you can download and unzip Ant to easily compile and run GWT applications.


이클립스를 기준으로 한다. 현재 최신 버전인 Luna 로 하겠다.





Installed the Google Plugin for Eclipse


The Google Plugin for Eclipse adds functionality to Eclipse for creating and developing GWT applications.


구글 플러그인을 깔자.


Help - Install New Software...



Work with: https://dl.google.com/eclipse/plugin/4.4


항목중에 Google Plugin for Eclise 4.4 를 체크 - Next 버튼 클릭.





Using the Archetype


http://mojo.codehaus.org/gwt-maven-plugin/user-guide/archetype.html


메이븐 archetype 를 업데이트 해줘야 한다. (현재 최신 버전은 2.7.0 이다.)


File - New - Other...


Maven Project 선택.



gwt-maven 으로 필터링을 하게 되면 하나가 나오는데 버전이 2.5.0 일 것이다. (아래처럼 2.7.0 이라면 패스)


Add Archetype 버튼 클릭.



Archetype Group Id: org.codehaus.mojo

Archetype Artifact Id: gwt-maven-plugin

Archetype Version: 2.7.0 (현재의 최신 버전)



OK 버튼 클릭하면 archetype 을 다운로드해서 업데이트 한다.

아직 프로젝트는 안만들 것이니 Canel 버튼 클릭.




Creating the StockWatcher application (using Eclipse)


File - New  - Other... - Maven Project


Group Id: com.google.gwt.sample (아무거나 -_-)

Artifact Id: StockWatcher

Package: com.google.gwt.sample.stockwatcher

module: StockWatcher


※ 여기서 모듈명은 GWT 튜토리얼에 있는 StockWatcher 로 하겠다.



Finish 버튼 클릭.




Using the Google Eclipse Plugin


http://mojo.codehaus.org/gwt-maven-plugin/eclipse/google_plugin.html


메이븐을 이용해서 만들었는데 이클립스의 개발자 모드로 해보려면 추가 작업이 필요하다.


resources 쪽에 있는 gwt.xml 파일을 java 쪽으로 옮겨야한다.

구글 플러그인에서 resources 에 있는 gwt.xml 을 인식하지 못하는 것 같다.


src/main/resources/com/google/gwt/sample/stockwatcher 아래에 있는 StockWatcher.gwt.xml 파일을

com.google.gwt.sample.stockwatcher 패키지로 이동시킨다.



이동 결과



다음은 Google 플러그인 연결(?) 작업이다.


StockWatcher 프로젝트에서 오른쪽 버튼 클릭 - Properties



Google - Web Application


This project has a War directory 체크



Google - Web Toolkit


Use Google Web Toolkit 체크

Entry Point Modules 에 StockWatcher 가 등록되어 있다.



OK 버튼 클릭


※ OK 버튼 클릭 시 NullPointException 에러가 나면 취소 후 위 작업을 다시 해보자.




Testing the default project components


StockWatcher 프로젝트에서 오른쪽 버튼 클릭 - Run As - Web Application (GWT Super Dev Mode)



WAR 디렉토리를 선택하라고 최초에 한번 나올 것이다. src/main/webapp(기본) 를 선택하자.


앞으로 여기에 모든 파일들이 적용 된다. 라이브러리, 컴파일 되는 자바스크립트 파일 등등..



Console 창에 로그 출력 되다가 Development Mode 창에 아래와 같이 주소가 나오면 성공!



더블클릭 해서 브라우저를 띄워보자.



이제 GWT 로 "Hello World" 찍었다.


'Framework' 카테고리의 다른 글

GWT with Maven  (0) 2015.03.18
tag: GWT, maven, Tutorial

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

AJAX Login with Spring Security

Posted at 2013.12.12 02:52 | Posted in Framework/Spring Security

Introduction


스프링 시큐리티를 이용하여 로그인을 처리할 때에 AJAX 방식으로 로그인 하는 방법이다.


크게 2가지로 볼 수 있겠다. ㅎㅎ




Using Handler


기본적인 10단계의 필터 체인 중에 UsernamePasswordAuthencationFilter 단계의 "authentication-success-handler-ref"와 "authentication-success-handler-ref" 를 이용하는 방법이다.


	<security:http auto-config="true" disable-url-rewriting="true" use-expressions="true">
		<security:intercept-url pattern="/login.*" access="permitAll" />		
		<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
		<security:form-login login-page="/login.html"
			username-parameter="id"
			password-parameter="password"
			login-processing-url="/login.ajax" default-target-url="/index.html"
			authentication-success-handler-ref="loginSuccessHandler"
			authentication-failure-handler-ref="loginFailureHandler" />
		<security:logout invalidate-session="true"
			logout-url="/logout"
			logout-success-url="/" />
	</security:http>
	
	<!-- 로그인 성공시 핸들러 -->
	<bean id="loginSuccessHandler" class="ajax.login.security.LoginSuccessHandler" />
	<!-- 로그인 실패시 핸들러 -->
	<bean id="loginFailureHandler" class="ajax.login.security.LoginFailureHandler" />


클래스 구조를 보면 아래와 같다.




아래와 같은 필터 체인의 순서롤 작동하게 된다.




구현 방법은 각각의 핸들러에서 응답을 JSON[각주:1]이나 원하는 포멧으로 만들어서 출력하면 된다.


public class LoginSuccessHandler implements AuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
		HttpServletResponse response, Authentication authentication) throws IOException,
		ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", true);
		map.put("returnUrl", getReturnUrl(request, response));

		// {"success" : true, "returnUrl" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}

public class LoginFailureHandler implements AuthenticationFailureHandler {

	@Override
	public void onAuthenticationFailure(HttpServletRequest request,
		HttpServletResponse response, AuthenticationException exception)
		throws IOException, ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", false);
		map.put("message", exception.getMessage());

		// {"success" : false, "message" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());

	}

}


ajax.login.1.war


ajax.login.1.zip





Using @MVC


다른 방법으로 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법이 있다.


개인적으로 이 두번째 방법을 선호한다. 왜냐하면 요청을 "application/json" 으로 유동적으로 한다던가 입출력관련 AOP 등등을 내 맘대로 사용할 수 있다. 그냥 컨트롤러니까~


@Controller
public class LoginController {

	@RequestMapping(value = "/login.html", method = RequestMethod.GET)
	public void loginPage(ModelAndView mav) {

	}

	@Autowired
	AuthenticationManager authenticationManager;
	@Autowired
	SecurityContextRepository repository;

	@RequestMapping(value = "/login.ajax", method = RequestMethod.POST)
	@ResponseBody
	public ModelMap login(HttpServletRequest request, HttpServletResponse response,
		@RequestParam(value = "id") String username,
		@RequestParam(value = "password") String password) {

		ModelMap map = new ModelMap();

		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
			username, password);

		try {
			// 로그인
			Authentication auth = authenticationManager.authenticate(token);
			SecurityContextHolder.getContext().setAuthentication(auth);
			repository.saveContext(SecurityContextHolder.getContext(), request, response);

			map.put("success", true);
			map.put("returnUrl", getReturnUrl(request, response));
		} catch (BadCredentialsException e) {
			map.put("success", false);
			map.put("message", e.getMessage());
		}

		return map;
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}


스프링 시큐리티 설정 하는 부분에서 그냥 <security:http> 를 써도 되지만 사용하지 않는 UsernamePasswordAuthenticationFilter 같은 필터 체인을 사용하지 않기 위해 수동으로 구성해 보았다. 꽤 어렵... -_-;; (자세한 내용은 context-security.xml 참조!)


ajax.login.2.war


ajax.login.2.zip





그 밖에도 요청 헤더를 까서 요청이 AJAX 인지 아닌지를 판단해서 다른 처리를 하는 것도 있고, 구현할 수 있는 방법은 무궁무진하다.


  1. JSON(제이슨, JavaScript Object Notation)은, 인터넷에서 자료를 주고받을 때 그 자료를 표현하는 방법이다. 자료의 종류에 큰 제한은 없으며, 특히 컴퓨터 프로그램의 변수값을 표현하는 데 적합하다. 그 형식은 자바스크립트의 구문 형식을 따르지만, 프로그래밍 언어나 플랫폼에 독립적이므로 C, C++, C#, 자바, 자바스크립트, 펄, 파이썬 등 많은 언어에서 이용할 수 있다. [본문으로]

'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 준영
    시큐리티 관련 찾고 있던 정보인데 감사합니당 ㅋ
  2. 비밀댓글입니다
  3. 황진식
    위의 문의를 비밀 글로 남겨 드렸었는데... 비밀번호입력시 오타로 확인 할수가 없어 다시 남겨드립니다. ㅠㅠ

    위의 첫번째 예를 다운 받아

    <security:session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.html">
    <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.html"/>
    </security:session-management>

    추가 하여 session 테스트를 진행 하였습니다만 두번째 로그인도 정상적으로 로그인이 됩니다.
    이부분에 대해 도움을 요청드릴수 있을까요?

    믈론
    <listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
    </listener>

    등록 한 상태 입니다.
    • 2014.05.04 18:41 신고 [Edit/Del]
      http://stackoverflow.com/questions/19319849/spring-security-3-1-session-concurrency-control-not-working-why

      동시 세션 제어를 하려면 인증의 주체가 되는 클래스를 비교 가능하게 hascode 와 equals 메소드를 구현해 주면 되네요...

      위의 ajax.login 프로젝트에서는 ajax.login.security.LoginToken 클래스죠 ^^;;

      @Override
      public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((id == null) ? 0 : id.hashCode());
      return result;
      }

      @Override
      public boolean equals(Object obj) {
      if (this == obj)
      return true;
      if (obj == null)
      return false;
      if (getClass() != obj.getClass())
      return false;
      LoginToken other = (LoginToken) obj;
      if (user == null) {
      if (other.user != null)
      return false;
      } else if (!user.equals(other.user))
      return false;
      return true;
      }

      물론 User 클래스도 hashcode 와 equals 가 구현 되어 있습니다.
  4. ㅎㅎ
    안녕하세요 좋은내용 잘봤습니다 ^^
    하나 궁금한게 있어서 여쭤볼려고하는데요
    먼저 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법을 따라해봤는데요
    저 같은경우엔 password를 StandardPasswordEncoder 로 암호화해서 db에 저장했습니다
    아시다시피 StandardPasswordEncoder 같은경우에 복호화가 안되서요
    요런경우엔 어떤식으로 처리하면 가능할지;
    어드바이스좀 부탁드리겠습니다 감사합니다
    • 2014.06.15 01:06 신고 [Edit/Del]
      제가 만들 샘플의 MySqlPasswordEncoder 도 복호화가 안됩니다.

      로그인시 디비에 들어있는 비밀번호를 복호화해서 비교하는게 아니라, 입력받은 비밀번호를 암호화 해서 디비의 비밀번호와 비교합니다.

      {입력받은 비번} == 복호화({디비에 있는 비번}) (x)
      암호화({입력받은 비번}) == {디비에 있는 비번} (o)

      이게 원하시는 답변인지는 모르겠네요 ㅠㅠ
  5. ㅎㅎ
    답변감사합니다 ^^

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Spring Security Session Destroy

Posted at 2013.10.27 12:39 | Posted in Framework/Spring Security

Intoduction


Spring Security 사용 중에 "사용자의 로그인 시간과 로그아웃 시간을 기록해야 한다" 라는 임무가 떨어졌다 -_-


문제는 이 로그아웃 이라는게 약간 골치 아프다.. 사용자가 직접 로그아웃 버튼을 클릭해서 로그아웃을 한다면 나이스 하지만...


대부분은 용무가 끝나면 그냥 브라우저를 끄거다 바로 다른 사이트로 넘어가게 된다. 이 경우에는 어떻게 하지?


개발자의 눈으로 보면 로그아웃 == 세션 만료로 볼 수 있다.


다른 사이트로 가거나 브라우저를 꺼버리게 되면 WAS의 기준으로 일정 시간이 지나세션이 만료되게 된다.


이 세션 만료를 캐취해서 처리를 하는 방법을 알아보자.




Using HttpSessionListener


서블릿의 세션 리스너를 이용한 방법이다.


아래와 같이 HttpSessionListener 인터페이스를 구현한 클래스를 만들고 web.xml 에 등록하면 된다.


스프링 시큐리티를 사용하지 않는다면 아래의 방법으로 처리하면 된다.


java


package session.destory.servlet;

import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.context.support.WebApplicationContextUtils;

import session.destory.entity.LoginHistory;
import session.destory.security.LoginToken;
import session.destory.service.LoginHistoryService;

public class SessionManagerListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// nothing
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// session
		HttpSession session = se.getSession();
		// spring web application context
		ApplicationContext context = WebApplicationContextUtils
			.getWebApplicationContext(se.getSession().getServletContext());
		// security context
		SecurityContext sc = (SecurityContext) session
			.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
		if (sc != null) {
			// authentication
			Authentication auth = sc.getAuthentication();
			// login history token
			LoginToken loginToken = (LoginToken) auth.getPrincipal();
			LoginHistory lh = loginToken.getHistory();
			lh.setLogoutDate(new Date());
			// update
			LoginHistoryService loginHistoryService = context
				.getBean(LoginHistoryService.class);
			loginHistoryService.modify(lh);
		}

	}

}


web.xml


<!-- servlet listener --> <listener> <listener-class>session.destory.servlet.SessionManagerListener</listener-class> </listener>


리스너에서 수동적으로 스프링 컨택스트와 시큐리티 컨텍스를 꺼내는게 맘에 안드는군... -_-/




Using HttpSessionEventPublisher


이번에는 스프링 시큐리티에서 지원하는 방법으로 해보겠다~!


ApplicationListener<SessionDestroyedEvent> 인터페이스를 구현한 클래스를 만들고 web.xml 에는 HttpSessionEventPublisher 클래스를 등록한다.


java


public class SessionDestoryListener implements ApplicationListener<SessionDestroyedEvent> {

	private LoginHistoryService loginHistoryService;

	@Autowired
	public void setLoginHistoryService(LoginHistoryService loginHistoryService) {
		this.loginHistoryService = loginHistoryService;
	}

	@Override
	public void onApplicationEvent(SessionDestroyedEvent event) {

		List<SecurityContext> contexts = event.getSecurityContexts();
		if (contexts.isEmpty() == false) {
			for (SecurityContext ctx : contexts) {
				Authentication atuh = ctx.getAuthentication().getPrincipal();
				// ...
			}
		}

	}

}

 

spring.xml

 

<beans ...>
	<bean id="sessionDestoryListener" class="session.destory.security.SessionDestoryListener" />
</beans>

 

web.xml


<!-- spring security event -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>


뭔가 스프링스러워진 것 같다 -_-v


- 로그인 처리



- 로그아웃 처리






아래는 샘플 소스다. Tomcat 7, WebLogic 12c 에서 테스트 해봄.


war - 실행해 볼 수 있는 war 파일. 7-zip 으로 분할 압축함.


session.destory.zip.001


session.destory.zip.002



src.zip - Maven 구조로 만들어진 소스 압축함.


session.destory.src.zip


 

... 소스 만들고 나니까 오타가 있네.... Destory → Destroy


'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 궁금합니다!!
    안녕하세요
    세션 만료시 로그아웃 시간 입력을 위해 소스를 참고 했는데요,
    잘 안되는 부분이 있어서 질문좀 드리려고 합니다. ^^;
    spring security / spring 프레임웍을 사용중입니다.
    처음에 ApplicationListener<SessionDestroyedEvent>를 사용해서 구현했는데 SecurityContext 리스트가 계속 null이더라구요.
    결국 세션에서 얻어오는 event.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
    방법으로 security context를 얻어왔습니다.
    궁금한게 getSecurityContexts() 로 리스트를 얻어올때는 null 이었는데 왜 세션에서 얻어오는 방법으로는 얻을수 있는지 모르겠습니다. 혹시 참고할만한 부분이나 조언을 해주시면 감사하겠습니다!
    • 2014.04.03 14:17 신고 [Edit/Del]
      1. 혹시 web.xml 에 리스너 등록 하셨죠?
      <listener>
      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
      </listener>

      2. 잠깐 찾아보니 스프링 시큐리티 버전에 따른 문제가 있는 것 같기도 합니다. (아닐지도 모름 -_-ㄷㄷ)

      3. 저도 퇴근해서 이것저것 해보겠습니다.
  2. 궁금합니다!!
    넵 감사합니다!
    리스너는 등록한 상태구요, 사용중인 spring security 버전은 3.1.0입니다.
    getSecurityContexts()가 왜 계속 null인지.. 계속 다시 해봤는데 모르겠네요
    getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) 과 내부적으로 같은 거라면 그냥 쓰려고 합니다.
    블로그 올려두신 내용들 많이 참고했습니다!! 감사합니다!

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

MySql Password Encoder

Posted at 2013.09.21 19:24 | Posted in Framework/Spring Security
개인적으로 자주 사용하는 Spring Security Password Encoder 입니다.

MySql 의 password() 펑션 알고리즘 사용합니다.

import java.security.GeneralSecurityException; import java.security.MessageDigest; import org.springframework.security.crypto.password.PasswordEncoder; public class MySqlPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new NullPointerException(); } byte[] bpara = new byte[rawPassword.length()]; byte[] rethash; int i; for (i = 0; i < rawPassword.length(); i++) bpara[i] = (byte) (rawPassword.charAt(i) & 0xff); try { MessageDigest sha1er = MessageDigest.getInstance("SHA1"); rethash = sha1er.digest(bpara); // stage1 rethash = sha1er.digest(rethash); // stage2 } catch (GeneralSecurityException e) { throw new RuntimeException(e); } StringBuffer r = new StringBuffer(41); r.append("*"); for (i = 0; i < rethash.length; i++) { String x = Integer.toHexString(rethash[i] & 0xff).toUpperCase(); if (x.length() < 2) r.append("0"); r.append(x); } return r.toString(); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || rawPassword == null) { return false; } if (!encodedPassword.equals(encode(rawPassword))) { return false; } return true; } }


적용 예시..

	<security:authentication-manager alias="authenticationManager">
		<security:authentication-provider user-service-ref="userService">
			<security:password-encoder ref="passwordEncoder" />
		</security:authentication-provider>
	</security:authentication-manager>

	<bean id="passwordEncoder" class="MySqlPasswordEncoder">


'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

SpringDM Test Project - hello.osgi

Posted at 2013.04.07 21:16 | Posted in Framework/Spring OSGi

SpringDM 환경 구성 후 이것 저것 잘 되는지 테스트하기 위한 기본 샘플을 만들어 봤습니다.


처음에 잘 못하니까 무지하게 고생 하네요;;


일반적인 스프링 MVC 구조로 보면 아래와 같습니다.



하나의 프로젝트에 컨트롤러/서비스/퍼시스턴스(마이바티스) 등등 모든 구성이 다 되어있죠 ㅎㅎ


이 구조를 각 레이어 기준으로 OSGi 구조로 변경해 보았습니다.



web/service/persistent/core 4개의 번들로 나누어 만들어 봤습니다.


아래 그림은 그냥 사용할 데이터베이스 스키마입니다.



이제 하나하나 번들화 해서 작업 해봐야 겠습니다.


일단 <context:component-scan /> 안되네요... classpath*: 관련된 것은 다 안되는 듯 합니다... 아닌가?




작업하면서 추가한 번들 목록입니다.


pom.xml 보기


jar 파일 및 소스 파일입니다.


hello.osgi.jar.zip


hello.osgi.src.zip


jar 파일은 개발환경에서만 해서 실제 환경에 올리면 될지 모르겠네요 ;;;


'Framework > Spring OSGi' 카테고리의 다른 글

SpringDM Test Project - hello.osgi  (0) 2013.04.07
OSGi + Tomcat 6 + SpringDM 플랫폼 환경 구성  (0) 2013.03.25
tag: OSGi, springdm

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

이제서야 OSGi 에 관심이 생겨서 뭔가를 만들어보기 위해서 OSGi + Tomcat 6 + SpringDM[각주:1] 개발 환경을 구성해 봅니다.


Eclipse Indigo + Maven Integration for Eclipse





Create Project


File - New - Other... → Maven - Maven Project




[maven-archetype-quickstart] 선택합니다.



[Group Id], [Artifact Id] 아무거나 입력 합니다. -_-;;


[Package] 는 필요가 없습니다.





Create Target Definition


File - New - Other... → Plug-in Development - Target Definition



[Parent Folder]는 전에 만든 "spring-dm-platform" 프로젝트를 선택합니다.


File name: spring-dm-tomcat.target

Initalize the target definition with: Nothing



lib 폴더를 하나 생성합니다.


target 폴더는 메이븐 라이브러리가 복사되는 곳이고, lib 폴더는 메이븐으로 구하지 못한 라이브러를 넣어둘 것입니다.



spring-dm-tomcat.target 파일을 열어 편집합니다.


Target: SpringDM with Tomcat


오른쪽 Add... 버튼을 클릭해서 Locations 을 추가합니다.



Directory 선택



Variables.. 를 이용해서 현재 프로젝트 경로(project_loc)를 얻어내고 하위 폴더 target 을 입력합니다. (그냥 입력해도 됨..)



같은 방법으로 lib 폴더도 선택합니다.



파일 저장 후 Windows - Preferences - Plug-in Development - Target Platform


SpringDM with Tomcat 을 선택합니다.






Running OSGi Framework


이제 한번 실행시켜 봅시다.


Run - Run Configurations...



왼쪽 메뉴 중에 OSGi Framework 에서 오른쪽 버튼 클릭 - New



이름 적당히 지어 주시고 Run 을 클릭해 봅시다.



org.eclipse.osgi 플러그인이 없다고 에러가 납니다.





Setting Osgi Framework on Maven


이제 메이븐으로 OSGi Framework 라이브러리를 세팅합시다.


pom.xml 파일을 열어 아래와 같이 수정 합니다.


pom.xml 열기


메이븐에 대해서 설명하지는 않겠습니다. -_-;;


위의 라이브러리 저장소(repository)는 SpringSource Repository(FAQ)에서 확인할 수 있습니다.


이제 SpringSource Repository에서 번들(라이브러리)를 하나씩 찾아서 넣어야 합니다. ㅠ_ㅠ;;


검색하는 방법은 [Advanced Search]나 오른쪽 상단 [Quick Search] 을 이용해서 검색하면 됩니다.



일단 OSGi Framework 실행 했을 때 없다고한 플러그인 라이브러리를 검색해봅시다.


검색해서 나온 버전 중 가장 최신 버전을 선택합니다.



중간에 보면 Maven 부분이 있습니다. 이걸 복사해서 <dependencies> </dependencies> 사이에 붙여넣기 하면 됩니다. ㅎㅎ



<dependencies>

<!-- 라이브러리 설정 -->

<dependency>

<groupId>org.eclipse.osgi</groupId>

<artifactId>org.eclipse.osgi</artifactId>

<version>3.7.1.R37x_v20110808-1106</version>

</dependency>

</dependencies>


저장 후 pom.xml 파일에서 오른쪽버튼 클릭 후 Run As - Maven install 클릭 합니다.



그러면 target 폴더에 jar 파일이 복사가 됩니다.



이제 spring-dm-tomcat.target 파일을 열어봅시다.


아래와 같이 번들이 추가 되어있습니다.


※ 메이븐을 이용해서 빠르게 라이브러리를 바꿔치기 하다보면 번들(라이브러리)이 갱신이 안될 때가 있는데 그냥 파일 닫았다가 다시 열어주면 갱신이 되더군요....



맨위의 타이틀 오른쪽에 "Set as Target Platform"을 클릭해야 실제 적용이 됩니다. 주의!



이제 다시 Run Configurations 로 가봅시다. 추가한 번들이 올라와 있습니다. 선택 후 Run!



콘솔 창을 보면 OSGi Framework 가 실행되었습니다. ㅠ_ㅠ



이제 OSGi 할 준비가 된 것입니다. ㅋㅋ


톰켓이나 웹로직 같이 끄려면 빨간 버튼(Terminate)을 이용해서 끄면 됩니다.


나중에 톰켓를 띄우게 될텐데 OSGi Framework 한번 실행한 상태에서 한번 실행하면 톰켓 포트 충돌로 인한 에러를 볼 수 있습니다.




Setting SpringDM on Maven


바로 SpringDM을 적용해 봅시다!!


SpringSource Repository 에서 "spring", "spring-osgi", "slf4j" 를 검색해서 최신 버전을 적용 합니다.


pom.xml 열기


이제 pom.xml 에서 마우스 오른쪽 버튼 - Run As - Maven install 해서 jar 파일을 target 에 생기게 합니다.


그리고 spring-dm-tomcat.target 파일을 열어서 ${project_loc}/target 폴더에 플러그인 갯수가 늘어났는지 확인합니다.


만약 파일은 존재하는데 변경이 되지 안았다면 spring-dm-tomcat.target 파일 닫고 바로 다시 열어주면 카운트가 변경 되어 있을 겁니다.



상단 오른쪽의 "Set as Target Platform" 링크를 클릭하여 라이브러리를 적용시켜 줍니다.



Run Configurations 창으로 가서 추가된 번들(또는 라이브러리)를 선택합니다.


그 후 오른쪽 아래 "Validate Bundles" 버튼을 클릭합니다. 버튼명 그대로 번들들의 유효성을 검사하게 됩니다.



아래와 같이 어떤 라이브러리에서 필요한 라이브러리가 빠져있는지 나옵니다.



웹에 관련된 부분은 바로 이어서 할 것이므로 일단 이 번들은 빼고 구동(Run)합니다.



아래와 같이 나오면 성공! 웹쪽을 뺀 SpringDM 까지 올라갔습니다.


osgi> log4j:WARN No appenders could be found for logger (org.springframework.osgi.extender.internal.activator.ContextLoaderListener).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


osgi> ss


Framework is launched.


id State       Bundle

0 ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106

1 ACTIVE      org.springframework.transaction_3.2.1.RELEASE

2 ACTIVE      org.springframework.osgi.core_1.2.1

3 ACTIVE      org.springframework.osgi.extensions.annotations_1.2.1

4 ACTIVE      com.springsource.org.apache.log4j_1.2.16

5 ACTIVE      org.springframework.jdbc_3.2.1.RELEASE

6 ACTIVE      org.springframework.oxm_3.2.1.RELEASE

7 ACTIVE      org.springframework.aspects_3.2.1.RELEASE

8 ACTIVE      com.springsource.org.apache.commons.logging_1.1.1

9 ACTIVE      org.springframework.context.support_3.2.1.RELEASE

10 ACTIVE      org.springframework.core_3.2.1.RELEASE

11 ACTIVE      org.springframework.beans_3.2.1.RELEASE

12 ACTIVE      org.springframework.context_3.2.1.RELEASE

13 ACTIVE      org.springframework.aop_3.2.1.RELEASE

14 RESOLVED    com.springsource.slf4j.log4j_1.6.1

           Master=17

15 ACTIVE      org.springframework.expression_3.2.1.RELEASE

16 ACTIVE      org.springframework.orm_3.2.1.RELEASE

17 ACTIVE      com.springsource.slf4j.api_1.6.1

           Fragments=14

18 ACTIVE      com.springsource.org.aopalliance_1.0.0

19 ACTIVE      org.springframework.osgi.extender_1.2.1

20 ACTIVE      org.springframework.osgi.io_1.2.1


osgi> 




Configuration Log4j


OSGi Framework 를 실행 하면 아래와 같은 경고가 나오게 되는데 흔하디 흔한 log4j.properties / log4j.xml 파일 못 찾는 경고입니다.


log4j:WARN No appenders could be found for logger (org.springframework.osgi.extender.internal.activator.ContextLoaderListener).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


log4j.xml


첨부한 log4j.xml 파일을 프로젝트 폴더에 복사합니다.



Run Configurations 창의 Arguments 탭으로 이동합니다.


VM arguments 부분에 -Dlog4j.debug=true -Dlog4j.configuration=file:log4j.xml 를 추가합니다.


Working directory 부분은 현재 자기의 프로젝트 경로로 바꿔줍니다.



Workspace... 버튼 클릭 후 spring-dm-platform 프로젝트 폴더를 선택하면 됩니다.



이제 OSGi Framework 를 실행하면 로그가 출력 됩니다.


osgi> log4j: Using URL [file:log4j.xml] for automatic log4j configuration.

log4j: Preferred configurator class: org.apache.log4j.xml.DOMConfigurator

log4j: System property is :null

log4j: Standard DocumentBuilderFactory search succeded.

log4j: DocumentBuilderFactory is: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

log4j: debug attribute= "null".

log4j: Ignoring debug attribute.

log4j: reset attribute= "false".

log4j: Threshold ="null".

log4j: Retreiving an instance of org.apache.log4j.Logger.

log4j: Setting [org.springframework.osgi.web.tomcat.internal] additivity to [false].

log4j: Level value for org.springframework.osgi.web.tomcat.internal is  [debug].

log4j: org.springframework.osgi.web.tomcat.internal level set to DEBUG

log4j: Class name: [org.apache.log4j.ConsoleAppender]

log4j: Parsing layout of class: "org.apache.log4j.PatternLayout"

log4j: Setting property [conversionPattern] to [%d _ %t _ %p _ %c _ %m%n].

log4j: Adding appender named [console] to category [org.springframework.osgi.web.tomcat.internal].

log4j: Retreiving an instance of org.apache.log4j.Logger.

log4j: Setting [org.springframework] additivity to [false].

log4j: Level value for org.springframework is  [info].

log4j: org.springframework level set to INFO

log4j: Adding appender named [console] to category [org.springframework].

log4j: Level value for root is  [warn].

log4j: root level set to WARN

log4j: Adding appender named [console] to category [root].

2013-03-27 00:54:43,505 _ Start Level Event Dispatcher _ INFO

_ org.springframework.osgi.extender.internal.activator.ContextLoaderListener

_ Starting [org.springframework.osgi.extender] bundle v.[1.2.1]

2013-03-27 00:54:43,545 _ Start Level Event Dispatcher _ INFO

_ org.springframework.osgi.extender.internal.support.ExtenderConfiguration

_ No custom extender configuration detected; using defaults...

2013-03-27 00:54:43,550 _ Start Level Event Dispatcher _ INFO

_ org.springframework.scheduling.timer.TimerTaskExecutor _ Initializing Timer


osgi> 


여기까지 했으면 이제 웹을 제외한 SpringDM을 사용할 준비가 되었습니다.




Setting Web Platform


이제 거의 다 왔습니다.... 후우~.. 이제 웹도 되게 합시다!



Run Configurations 창에서 전 단계에서 선택하지 않았던 4개의 웹 관련 번들을 선택 후 검사합니다. (Validate Bundles 버튼)



아래에 나온 없는 패키지가 포함된 번들을 찾아줘야 합니다.



일단 서블릿 2.4 버전을 찾아서 넣어줍시다.


pom.xml 보기


이렇게 추가된 라이브러리를 적용한 후 다시 Run Configurations 창을 엽니다.


Add Required Bundles 버튼을 클릭하면 필요한 라이브러리를 자동으로 선택 해줍니다. (만약 있다면..)



이제 번들 유효성 검사를 해보면 아무 이상이 없다고 합니다.



실행! 잘 되는듯 하다가 에러가 나는군요...


2013-03-28 23:20:49,878 _ WebExtender-Init _ INFO

_ org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration

_ No custom extender configuration detected; using defaults...

Exception in thread "WebExtender-Init" java.lang.NoClassDefFoundError: org/apache/catalina/Loader

at o.s.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:194)

at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:105)

at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener$1.run(WarLoaderListener.java:361)

at java.lang.Thread.run(Unknown Source)

Caused by: java.lang.ClassNotFoundException: org.apache.catalina.Loader

at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)

at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)

at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)

at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)

at java.lang.ClassLoader.loadClass(Unknown Source)

... 4 more


톰켓 번들을 설치합시다.


내용이 너무 길어저 pom.xml 만 남깁니다.


pom.xml 열기


번들(라이브러리) 적용 후 우효성 검사를 하면 이상이 없다고 합니다.


보면 서블릿 번들이이 2.4/2.5 두개가 있는데 이중에 한가지만 선택하면 되겠습니다.



이제 실행해 봅시다.


WebExtender-Init _ ERROR _ o.s.osgi.web.deployer.tomcat.TomcatWarDeployer _ No Catalina Service found, bailing out

o.s.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable


WebExtender-Init _ ERROR _ o.s.osgi.web.extender.internal.activator.WarLoaderListener _ Cannot property start Spring DM WebExtender; stopping bundle...

o.s.osgi.OsgiException: Cannot create Tomcat deployer

Caused by: o.s.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable


WebExtender-Init _ INFO _ org.springframework.scheduling.timer.TimerTaskExecutor _ Cancelling Timer


org.apache.catalina.Service 서비스를 찾을 수 없다. 즉 톰켓이 안 떠있다는 겁니다.


catalina.start.osgi-6.0.16-20080327.121306-4.jar


첨부한 번들을 lib 폴더에 복사해주고 적용 시킵니다. (이건 메이븐에 없더군요...)



마지막으로 실행!


Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Starting Apache Tomcat/6.0.32 ...

Tomcat Catalina Start Thread _ DEBUG _ o.s.osgi.web.tomcat.internal.Activator _ Reading default server configuration from  bundleresource://45.fwk1110027070/conf/embedded-server-defaults.properties

2013. 3. 28 오후 11:40:53 org.apache.catalina.startup.Embedded start

정보: Starting tomcat server

2013. 3. 28 오후 11:40:53 org.apache.catalina.core.StandardEngine start

정보: Starting Servlet Engine: Apache Tomcat/6.0.32

2013. 3. 28 오후 11:40:53 org.apache.coyote.http11.Http11Protocol init

정보: Initializing Coyote HTTP/1.1 on http-127.0.0.1-8080

2013. 3. 28 오후 11:40:53 org.apache.coyote.http11.Http11Protocol start

정보: Starting Coyote HTTP/1.1 on http-127.0.0.1-8080

Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Succesfully started Apache Tomcat/6.0.32

WebExtender-Init _ INFO _ o.s.osgi.service.importer.support.OsgiServiceProxyFactoryBean _ Found mandatory OSGi service for bean []

Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Published Apache Tomcat/6.0.32 as an OSGi service

WebExtender-Init _ INFO _ o.s.osgi.web.deployer.tomcat.TomcatWarDeployer _ Found service Catalina


후우... 성공!


web.xml


마지막으로 첨부한 web.xml 파일을 conf/web.xml 에 복사합니다. (그냥 톰켓에 있는 web.xml 입니다)



이 파일이 있어야 나중에 웹어플 배치했을 때 정상적으로 됩니다.


안그러면 배치했을 때 아래 로그와 함께 영원히 404를 볼 수 있습니다.


[ INFO - WebExtender-Init] org.springframework.osgi.web.extender.internal.activator.WarLoaderListener - hello.web (hello.web) is a WAR, scheduling war deployment on context path [/hello.web] (web.xml found at [bundleentry://11.fwk854535264/WEB-INF/web.xml])

2013. 3. 30 오전 2:03:35 org.apache.catalina.startup.ContextConfig defaultWebConfig

정보: No default web.xml

[ INFO - Timer-0] org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer - Successfully deployed bundle [hello.web (hello.web)] at [/hello.web] on server org.apache.catalina.startup.Embedded/1.0




Trouble Shooting


설정을 바꿨는데도 안바뀐다면?


Run Configurations 의 Settings 탭에서 "Clear the configuration area before launching"를 체크해주세요.



중간 중간 아래와 같은 에러가 난다면? 하라는 대로 하면 됩니다. -_-;;





마지막으로 최종 pom.xml 파일 첨부합니다.


pom.xml



  1. 구) Spring OSGi / 신) Spring Dynamic Modules [본문으로]

'Framework > Spring OSGi' 카테고리의 다른 글

SpringDM Test Project - hello.osgi  (0) 2013.04.07
OSGi + Tomcat 6 + SpringDM 플랫폼 환경 구성  (0) 2013.03.25

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

출처 : http://onjava.com/lpt/a/6443



스프링 XML Configuration을 위한 12가지 최선의 실천사항들

by Jason Zhicheng Li
01/25/2006

Spring is a powerful Java application framework, used in a wide range of Java applications. It provides enterprise services to Plain Old Java Objects (POJOs). Spring uses dependency injection to achieve simplification and increase testability.property name="shippedBy" value="lizjason Spring beans, dependencies, and the services needed by beans are specified in configuration files, which are typically in an XML format. The XML configuration files, however, are verbose and unwieldy. They can become hard to read and manage when you are working on a large project where many Spring beans are defined.

스프링은 Java 애플리케이션의 많은 범위에서 사용하고 있는 강력한 Java 애플리케이션 프레임워크입니다. 이것은 POJO(Plan Old Java Objects)에 엔터프라이즈 서비스의 힘을 제공합니다. 스프링은 간결함과 테스트 용이성을 이루기 위해 의존성 주입(dependency injection)을 이용합니다. 스프링 빈들과 의존성 그리고 빈들이 필요한 서비스들은 configuration 파일들에 설정하는데 일반적으로 XML 형식으로 되어 있습니다. 어쨌거나 이 XML configuration 파일들은 장황하고 다루기 쉽지 않습니다. 큰 프로젝트에서 스프링 빈을 많이 정의할 경우 이 configuration은 읽기도 어렵고 관리하기도 어려워집니다.

In this article, I will show you 12 best practices for Spring XML configurations. Some of them are more necessary practices than best practices. Note that other factors, such as domain model design, can impact the XML configuration, but this article focuses on the XML configuration's readability and manageability.

이 기사에서 스프링 XML configuration에 대한 12가지 최선의 실천사항들을 소개할 것입니다.  그 중 일부는 최선의 실천사항이라기 보다는 필수 실천사항들입니다. 도메인 모델 설계같은 다른 요소의 영향도 고려해야 하지만 이 기사에서는 configuration의 가독성(readability)과 관리편의성(manageablitity)에 초점을 두었습니다.


1. Avoid using autowiring
Spring can autowire dependencies through introspection of the bean classes so that you do not have to explicitly specify the bean properties or constructor arguments. Bean properties can be autowired either by property names or matching types. Constructor arguments can be autowired by matching types. You can even specify the autodetect autowiring mode, which lets Spring choose an appropriate mechanism. As an example, consider the following:

스프링은 빈의 introspection을 통해 의존관계를 자동으로 묶을(autowirie) 수 있어 명시적으로 빈의 프로퍼티나 생성자 아규먼트를 지정하지 않아도 됩니다. 프로퍼티 이름이나 타입 매칭으로 빈의 프로퍼티를 자동 설정할 수 있습니다. 타입 패칭으로 생성자의 아규먼트를 자동 설정할 수 있습니다. 또한 자동탐지를 autowiring 모드로 지정하여 스프링이 적절한 메커니즘을 선택하게 할 수 있습니다. 다음 예제를 보십시오.

<bean id="orderService" class="com.lizjason.spring.OrderService" autowire="byName"/>

The property names of the OrderService class are used to match a bean instance in the container. Autowiring can potentially save some typing and reduce clutter. However, you should not use it in real-world projects because it sacrifices the explicitness and maintainability of the configurations. Many tutorials and presentations tout autowiring as a cool feature in Spring without mentioning this implication. In my opinion, like object-pooling in Spring, it is more a marketing feature. It seems like a good idea to make the XML configuration file smaller, but this will actually increase the complexity down the road, especially when you are working on a large project where many beans are defined. Spring allows you mix autowiring and explicit wiring, but the inconsistency will make the XML configurations even more confusing.

컨테이너에서 OrderService 클래스의 프로퍼티 이름으로 빈 인스턴스를 매치하도록 사용하고 있습니다. Autowiring은 어쩌면 타이핑하는 수고를 줄이고 난잡함을 줄일 수 있습니다.  그러나 이것은 configuration의 명확성과 유지보수성을 희생하기에 실제 프로젝트에서 사용하면 안됩니다. 많은 튜토리얼과 프리젠테이션에서 이러한 숨겨진 영향을 언급하지 않고 스프링의 쿨한 특징으로 적극 추천하고 있습니다. 사견으로 스프링의 객체 풀링 같이 이것은 마케팅을 위한 특징에 가깝습니다. 이것은 XML configuration 파일을 작게 만드는 좋은 아이디어 같지만 실제로 복잡함을 증가시킬 것입니다. 특히 많은 빈들이 정의된 대규모 프로젝트에서 더합니다. 스프링에서 autowiring과 명시적인 wiring을 섞어서 쓸 수도 있지만 이러한 일관성 없는 방식은 XML configuration을 더욱 혼란스럽게 만듭니다.

2. Use naming conventions
This is the same philosophy as for Java code. Using clear, descriptive, and consistent name conventions across the project is very helpful for developers to understand the XML configurations. For bean ID, for example, you can follow the Java class field name convention. The bean ID for an instance of OrderServiceDAO would be orderServiceDAO.For large projects, you can add the package name as the prefix of the bean ID.

이것은 java 코드에서와 같은 사상입니다. 프로젝트 전반에 거쳐 명확함, 선언적 그리고 일관된 명명규칙을 사용하면 개발자들이 XML configuration을 이해하는데 많은 도움을 줍니다. 빈 ID로 예를 들자면 Java 클래스의 필드 이름 규칙을 사용할 수 있을 것입니다. OrderServiceDAO 인스턴스의 빈 아이디는 orderServiceDAO가 될 것입니다. 대규모 프로젝트에서는 빈 ID의 접두어로 패키지 이름을 추가할 수 있을 겁니다.

3. Use shortcut forms
The shortcut form is less verbose, since it moves property values and references from child elements into attributes. For example, the following:

프로퍼티 값이나 레퍼런스를 자식 엘리먼트에서 속성으로 옮기기에 간단한 형식이 덜 장황합니다. 아래 예를 보십시오.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName">
            <value>lizjason</value>
        </property>
        <constructor-arg>
            <ref bean="orderDAO">
        </constructor-arg>
    </bean>

can be rewritten in the shortcut form as:

이것은 다음과 같이 간단한 형식으로 다시 작성할 수 있습니다. 

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

The shortcut form has been available since version 1.2. Note that there is no shortcut form for <ref local="...">.

간단한 형식은 1.2 버전부터 사용이 가능합니다. <ref local="...">을 위한 간단한 형식은 없다는 것을 염두해 두십시오.

The shortcut form not only saves you some typing, but also makes the XML configuration files less cluttered. It can noticeably improve readability when many beans are defined in a configuration file.

간단한 형식은 타이핑을 줄이는 것뿐만 아니라 XML configuration 파일을 덜 난잡하게 해 줍니다. Configuration 파일에 많은 빈들을 정의할 경우 눈에 띄게 가독성을 향상시킬겁니다.

4. Prefer type over index for constructor argument matching
Spring allows you to use a zero-based index to solve the ambiguity problem when a constructor has more than one arguments of the same type, or value tags are used. For example, instead of:

스프링은 생성자가 두 개이상의 같은 타입을 가질 때 모호한 문제를 풀기 위해 0부터 시작하는 index를 사용하는 것을 허용합니다. 예를 들면 다음과 같습니다.

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg index="0" value="lizjason"/>
        <constructor-arg index="1" value="100"/>
    </bean>

It is better to use the type attribute like this:


이것은 다음과 같이 type 속성을 사용하는게 더 낫습니다.

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <constructor-arg type="java.lang.String"
            value="lizjason"/>
        <constructor-arg type="int" value="100"/>
    </bean>

Using index is somewhat less verbose, but it is more error-prone and hard to read compared to using the type attribute. You should only use index when there is an ambiguity problem in the constructor arguments.

index를 사용하는 것이 어느 정도 더 간결하지만 더 에러가 발생하기 쉽고 type 속성에 비해 읽기도 어렵습니다. 생성자 아규먼트에 모호한 문제가 있을 경우에만 index를 사용하여야 합니다.

5. Reuse bean definitions, if possible
Spring offers an inheritance-like mechanism to reduce the duplication of configuration information and make the XML configuration simpler. A child bean definition can inherit configuration information from its parent bean, which essentially serves as a template for the child beans. This is a must-use feature for large projects. All you need to do is to specify abstract=true for the parent bean, and the parent reference in the child bean. For example:

Configuration 정보의 중복을 줄이고 XML configuration을 간결하게 하기 위해 스프링은 상속과 비슷한 메커니즘을 제공합니다. 자식 빈 정의는 부모 빈으로부터 configuration 정보를 상속받을 수 있습니다. 부모 빈 정의가 자식 빈을 위한 템플릿 역할을 합니다. 대규모 프로젝트에서는 반드시 사용하여할 기능입니다. 해야 할 일은 단지 부모 빈에 abstract=true로 설정하고 자식 빈에서 parent 로 참조하는 것입니다. 예를 들어보면.

    <bean id="abstractService" abstract="true"
        class="com.lizjason.spring.AbstractService">
        <property name="companyName"
            value="lizjason"/>
    </bean>     <bean id="shippingService"
        parent="abstractService"
        class="com.lizjason.spring.ShippingService">
        <property name="shippedBy" value="lizjason"/>
    </bean>

The shippingService bean inherits the value lizjason for the companyName property from the abstractService bean. Note that if you do not specify a class or factory method for a bean definition, the bean is implicitly abstract.

shippingService 빈은 abstractService 빈으로 부터 lizjason 값을 가진 companyName 속성을 상속받습니다. 만일 빈 정의에 클래스나 팩토리 메소드를 지정하지 않는다면 그 빈은 암시적으로 abstract이 된다는 것을 알아두십시오.

6. Prefer assembling bean definitions through ApplicationContext over imports
Like imports in Ant scripts, Spring import elements are useful for assembling modularized bean definitions. For example:

Ant 스크립트의 import같이 스프링의 import 엘리먼트는 빈 정의를 모듈화하여 조립하는데 유용합니다. 예를 들면:

    <beans>
        <import resource="billingServices.xml"/>
        <import resource="shippingServices.xml"/>
        <bean id="orderService"
            class="com.lizjason.spring.OrderService"/>
    <beans>

However, instead of pre-assembling them in the XML configurations using imports, it is more flexible to configure them through the ApplicationContext. Using ApplicationContext also makes the XML configurations easy to manage. You can pass an array of bean definitions to the ApplicationContext constructor as follows:

그런데, import를 이용하여 XML configuration 안에서 미리 조립하는 대신 ApplicationContext 클래스를 이용하여 구성하는 것이 보다 유연합니다. 또한 ApplicationContext를 이용하면 XML configuration을 관리하기 쉬워집니다. 다음과 같이 빈 정의의 배열을 ApplicationContext 생성자에 전달할 수 있습니다.

    String[] serviceResources =
        {"orderServices.xml",
        "billingServices.xml",
        "shippingServices.xml"};
    ApplicationContext orderServiceContext = new
        ClassPathXmlApplicationContext(serviceResources);


7. Use ids as bean identifiers
You can specify either an id or name as the bean identifier. Using ids will not increase readability, but it can leverage the XML parser to validate the bean references. If ids cannot be used due to XML IDREF constraints, you can use names as the bean identifiers. The issue with XML IDREF constraints is that the id must begin with a letter (or one of a few punctuation characters defined in the XML specification) followed by letters, digits, hyphens, underscores, colons, or full stops. In reality, it is very rare to run into the XML IDREF constraint problem.

빈 식별자로 id나 name을 지정할 수 있습니다. id를 사용하면 가독성을 향상시키지는 않지만 XML 파서가 빈 참조를 검사하는데 도움이 됩니다. XML IDREF 제약때문에 id를 사용할 수 없으면 빈 식별자로 name을 사용할 수 있습니다. XML IDREF 제약의 이슈는 id는 반드시 문자(또는 XML 스펙에 정의된 구두문자 중 하나)로 시작하여 뒤에 문자, 숫자, 하이픈, 밑줄, 콜론 또는 마침표가 옵니다. 사실, XML IDREF 제약의 문제에 빠지는 것은 매우 드문 일입니다.

8. Use dependency-check at the development phase
You can set the dependency-check attribute on a bean definition to a value other than the default none, such as simple, objects, or all, so that the container can do the dependency validation for you. It is useful when all of the properties (or certain categories of properties) of a bean must be set explicitly, or via autowiring

빈 정의에 dependency-check 속성을 기본값인 none 이나, simple, object 또는 all로 설정할 수 있습니다. 이는 dependency를 검증해 줍니다. 명시적으로든 자동(autowiring)으로 든  빈의 모든 속성(또는 속성의 어떤 카테고리들)에 반드시 값이 설정되어야 할 때 유용합니다.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService"
        dependency-check="objects">
        <property name="companyName"
            value="lizjason"/>
        <constructor-arg ref="orderDAO"/>
    </bean>

In this example, the container will ensure that properties that are not primitives or collections are set for the orderService bean. It is possible to enable the default dependency check for all of the beans, but this feature is rarely used because there can be beans with properties that don't need to be set.

이 예제에서 컨테이너는 기본 타입(primitive)이나 집합 타입이 아닌 orderService 빈의 속성 값은 설정될 것을 보장합니다. 기본으로 모든 빈에 dependency 검사를 하도록 할 수도 있지만, 반드시 설정할 필요가 없는 빈의 속성도 있을 수 있기 때문에 이 기능은 거의 사용하지 않습니다.

9. Add a header comment to each configuration file
It is preferred to use descriptive ids and names instead of inline comments in the XML configuration files. In addition, it is helpful to add a configuration file header, which summarizes the beans defined in the file. Alternatively, you can add descriptions to the description element. For example:

XML configuration 파일 중간에 주석을 다는 것보다는 딱 봐도 알 수 있는(descriptive) id나 이름을 사용하는 것이 더 좋습니다. 거기에다 파일의 헤더에 빈 정의에 대한 요약을 다는 것도 많은 도움이 됩니다.
대신 다음과 같이 description 엘리먼트로 설명을 추가할 수도 있습니다.

    <beans>
        <description>
            This file defines billing service
            related beans and it depends on
            baseServices.xml,which provides
            service bean templates...
        </description>
        ...
    </beans>

One advantage of using the description element is that it is easy to for tools to pick up the description from this element.

이 description 엘리먼트를 사용하면 각종 도구에서 설명을 뽑아내기 쉬운 장점이 있습니다.

10. Communicate with team members for changes
When you are refactoring Java source code, you need to make sure to update the configuration files accordingly and notify team members. The XML configurations are still code, and they are critical parts of the application, but they are hard to read and maintain. Most of the time, you need to read both the XML configurations and Java source code to figure out what is going on.

Java 소스 코드를 리팩토링할 때 관련된 configuration 파일들을 갱신할 필요가 있으면 팀 동료에게 알려야 합니다. XML configuration 역시 코드이며 애플리케이션에서 중요한 요소 중 하나이지만 읽기도 어렵고 유지하기도 어렵습니다. 대부분의 시간동안 뭐가 어떻게 돌아가는지 파악하기 위해 XML configuration과 Java 코드 둘 다 읽어야 할 필요가 있습니다.

11. Prefer setter injection over constructor injection
Spring provides three types of dependency injection: constructor injection, setter injection, and method injection. Typically we only use the first two types.

스프링은 생성자 주입, setter 주입 그리고 메소드 주입 세 가지 의존성 주입(dependency injection) 방법을 제공합니다. 전형적으로 앞의 두 가지 방식만 사용합니다.

    <bean id="orderService"
        class="com.lizjason.spring.OrderService">
        <constructor-arg ref="orderDAO"/>
    </bean>

    <bean id="billingService"
        class="com.lizjason.spring.BillingService">
        <property name="billingDAO"
            ref="billingDAO">
    </bean>


In this example, the orderService bean uses constructor injection, while the BillingService bean uses setter injection. Constructor injection can ensure that a bean cannot be constructed in an invalid state, but setter injection is more flexible and manageable, especially when the class has multiple properties and some of them are optional.

이 예제에서 orderService 빈은 생성자 주입을 사용한 반면 billingService는 setter 주입을 사용합니다. 생성자 주입의 경우 올바르지 않은 상태로는 생성할 수 없도록 보증하지만 setter 주입이 보다 유연하고 관리하기 쉽습니다. 특히 클래스가 여러 개의 속성을 가지고 그 중 몇 개는 선택적일 경우 더욱 그렇습니다.

12. Do not abuse dependency injection
As the last point, Spring ApplicationContext can create Java objects for you, but not all Java objects should be created through dependency injection. As an example, domain objects should not be created through ApplicationContext. Spring is an excellent framework, but, as far as the readability and manageability are concerned, the XML-based configuration can become an issue when many beans are defined. Overuse of dependency injection will make the XML configuration more complicated and bloated. Remember, with powerful IDEs, such as Eclipse and IntelliJ, Java code is much easier to read, maintain, and manage than XML files!

마지막으로, 스프링 ApplicationContext 는 여러분을 위해 Java 객체를 생성해 줍니다. 그러나 모든 Java 객체가 의존성 주입을 통해 생성해야 하는 것은 아닙니다. 그 예로 도메인 객체는 ApplicationContext를 통해 생성하지 말아야 합니다. 스프링은 뛰어난 프레임워크이나 많은 빈들이 정의되면 XML 기반의 configuration이 이슈가 될 수 있으므로 어느 정도 가독성과 관리성을 고려해야 합니다. 의존성 주입을 과용하면 XML configuration은 보다 복잡해지고 비대해질 것입니다. Eclipse나 InteliJ와 같은 강력한 IDE를 사용하면 Java 코드가 XML 파일보다 훨씬 읽거나 유지하거나 관리하기가 쉽다는 것을 기억하십시오!

Conclusion
XML is the prevailing format for Spring configurations. XML-based configuration can become verbose and unwieldy when many beans are defined. Spring provides a rich set of configuration options. Appropriately using some of the options can make the XML configurations less cluttered, but other options, like autowiring, may reduce readability and maintainability. Following good practices discussed in the article may help you to create clean and readable XML configuration files!

XML은 일반적인 Spring configuration 포맷입니다. 많은 빈들을 정의할 경우 XML 기반 configuration은 장황해지고 다루기 어려워지기 쉽습니다. 스프링은 풍부한 configuration 옵션들을 지원합니다. 적절히 이 옵션들을 사용하면 XML configuration은 덜 복잡해지기도 하지만 autowiring 같은 다른 옵션들은 가독성이나 관리성을 떨어뜨립니다. 이 기사에서 논의한 좋은 실천사항들을 따른다면 깨끗하고 가독성이 좋은 XML configuration 파일들을 만드는데 도움이 될 것입니다!

Resources
The weblog post this article is based upon
The Spring framework website for further information about Spring XML configurations
The XML specification for IDREF constraints

Jason Zhicheng Li is a senior software engineer with Object Computing, Inc. in St. Louis, MO. 

'Framework > Spring' 카테고리의 다른 글

Twelve Best Practices For Spring XML Configurations  (0) 2010.06.23

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

PK 두개 이상시 매핑

Posted at 2009.12.24 14:04 | Posted in Framework/Hibernate


위와 같은 PK(Primary Key)가 두개인 테이블이 있습니다.

이 테이블을 매핑시켜봅시다.

PK가 하나일때는 <id> 태그를 써서 쓰면 되지만, 두개일때는 <composite-id> 태그를 사용합니다.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="model">
	<class name="Code" table="common_code">

		<composite-id>
			<!-- PK1 -->
			<key-property name="code" type="string">
				<column name="code" length="10">
					<comment>코드</comment>
				</column>
			</key-property>
			<!-- PK2 -->
			<key-property name="lang" type="string">
				<column name="language" length="3">
					<comment>언어</comment>
				</column>
			</key-property>
		</composite-id>
			
		<property name="codeDesc" type="text">
			<column name="code_desc" not-null="false">
				<comment>코드 설명</comment>
			</column>
		</property>

	</class>
</hibernate-mapping>

그리고 클래스 파일은 Serializable 인터페이스를 구현해야 한답니다....

package model;

import java.io.Serializable;

public class Code implements Serializable {

    private static final long serialVersionUID = -14882784587978705L;
 
    private String code; // PK1
    private String lang; // PK2
    private String codeDesc;

    // getter, setter
}

'Framework > Hibernate' 카테고리의 다른 글

PK 두개 이상시 매핑  (2) 2009.12.24
Reverse Engineering  (0) 2009.12.24
Hibernate 시작하기  (0) 2009.12.23
Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
Criteria 사용하여 질의 하기 #1  (3) 2009.09.10
다대다(n:m) 관계 설정하기  (0) 2009.09.02
일대다(1:n) 관계 설정하기  (1) 2009.08.31
일대일(1:1) 관계 설정하기  (0) 2009.08.27
하이버네이트(Hibernate) 사용하기  (6) 2009.08.24
  1. 비밀댓글입니다
  2. agapeuni@naver.com
    좋은글 출처를 표시하고 블로그에 담아갑니다. ^^

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Reverse Engineering

Posted at 2009.12.24 09:57 | Posted in Framework/Hibernate


이미 만들어져 있는 데이터베이스의 테이블을 이용해 java 파일과 매핑 xml 파일을 생성시켜봅시다~



일단 http://antop.tistory.com/53 에서 아래 3단계까지는 하셔야 합니다 ㅎㅎ

- 하이버네이트 플러그인 설치
- 하이버네이트 라이브러리 세팅
- 하이버네이트 기본 설정파일 작성

설정파일(hibernate.cfg.xml)까지 만들었으면

File - New - Other... - Hibernate - Hibernate Console Configuration - Next




하이버네이트 콘솔 정보는 hibernate.cfg.xml 설정 파일을 사용합니다.




하이버네이트 콘솔이 뭐냐... 하면 하이버네이트 플러그인을 설치하면 볼 수 있는 Hibernate Perspective 에서 사용하는 데이터베이스 접속 정보 정도로 보면 되겠습니다.

난중에 여기서 HQL 쿼리도 날려보고 여러가지를 할 수 있습니다.


이제 이 콘솔 정보를 기반으로 Reverse Engineering(이하 리버스) 파일을 생성 합니다.

File - New - Other... - Hibernate - Hibernate Reverse Engineering File (reveng.xml) - Next




리버스 파일을 저장 할 폴더 지정 - Next




인제 여기서 console configuration 에서 셀렉트박스를 눌러보면 방금전에 만든 콘솔 정보가 있을 겁니다.

콘솔정보 선택 후 Refresh 클릭하면 DB에 있는 테이블 목록을 받아옵니다.

리버스하고싶은 테이블을 선택 후 Finish




그럼 hibernate.reveng.xml 파일이 열리는데 걍 닫기.


이제 Hibernate Perspective 로 이동합니다.

아래 그림 처럼 [그.. 아이콘] 클릭 후 -_-... "Hibernate Code Generation Configurations..." 클릭




아무것도 없습니다.... "새로 만들기" 버튼 클릭




적당히 이름 정하고, 콘솔정보 선택하시고, 만들어지는 파일들을 저장할 폴더 위치를 지정합니다.

※ 아래 그림의 Name 처럼 하면 이미 있는 이름이라고 안됩니다. 딴거 쓰세요 ㅠ_ㅠ




src 폴더(최상위)를 선택합니다.




package 경로를 지정합니다.

※ 패키지를 지정하면 패키지 폴더도 생성 됩니다.

이제 리버스 엔진 파일 아까 만들었던걸 지정해줍시다.




새로 만들기를 원하냐고 물어보는데 전 이미 만들었으니 "Use existing..." 클릭




파일을 선택합니다.




이제 Exporters 탭으로 이동해서 어떤 파일을 생성할지 선택해줍니다.




Run 클릭...



신나게 파일을 생성하고 있습니다.




파일이 생성되었습니다.

뭐 에러나는 것들도 있네요.. 컴퓨터가 만능은 아니자나여!


'Framework > Hibernate' 카테고리의 다른 글

PK 두개 이상시 매핑  (2) 2009.12.24
Reverse Engineering  (0) 2009.12.24
Hibernate 시작하기  (0) 2009.12.23
Criteria 사용하여 질의 하기 #2  (1) 2009.10.03
Criteria 사용하여 질의 하기 #1  (3) 2009.09.10
다대다(n:m) 관계 설정하기  (0) 2009.09.02
일대다(1:n) 관계 설정하기  (1) 2009.08.31
일대일(1:1) 관계 설정하기  (0) 2009.08.27
하이버네이트(Hibernate) 사용하기  (6) 2009.08.24

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기