Maven 빌드 중 아래 오류가 발생함.

[DEBUG] incrementalBuildHelper#beforeRebuildExecution

[INFO] Compiling 47 source files to C:\dev\workspace-class\spring-mybatis-grid\target\classes

[DEBUG] incrementalBuildHelper#afterRebuildExecution

[INFO] /C:/[경로]/java/com/edu/test/NamingReflection.java: C:\dev\workspace-class\spring-mybatis-grid\src\main\java\com\edu\test\NamingReflection.java uses unchecked or unsafe operations.

[INFO] /C:/[경로]/java/com/edu/test/NamingReflection.java: Recompile with -Xlint:unchecked for details.

[INFO] -------------------------------------------------------------

[ERROR] COMPILATION ERROR :

[INFO] -------------------------------------------------------------

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[12,17] package org.junit does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[13,24] package org.junit.runner does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[14,40] package org.springframework.test.context does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[15,47] package org.springframework.test.context.junit4 does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[20,2] cannot find symbol symbol: class RunWith

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[21,2] cannot find symbol symbol: class ContextConfiguration


오류나는 클래스를 살펴보니 모두 TEST 케이스에서만 에러난다.

패키지를 못 읽어 오는 것이기에 pom.xml 을 수정해야 한다.

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency>

에서 scope 를 삭제한다. 아래처럼  

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency>


SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation


jxls 라이브러리를 사용하면성 기존에 있던 log4j와 jxls에서 사용하는 slf4j가 충돌아닌 충돌이 나더라...



로그에 나온 링크로 가보면 저렇게 써 있을 뿐 정확한 안내는 없다.
(실은 저게 정확할 수도..?)

라이브러리 로드 순서도 영향이 있을까 싶어 아래처럼 pom.xml 을 수정하니 정상적으로 로그가 출력된다.

		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>slf4j-api</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>jcl-over-slf4j</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>slf4j-log4j12</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/log4j/log4j -->
		<dependency>
		    <groupId>log4j</groupId>
		    <artifactId>log4j</artifactId>
		    <version>1.2.17</version>
		</dependency>


잘 된다... 


이런 에러가 발생 했을 경우.. 

to enable the nesting exclude '......./' from ~~~

 
pom.xml 에 아래 태그를 삭제한다.

<sourceDirectory>src</sourceDirectory> 

해결..

현재 작업하고 있는 프로젝트의 모든 프레임워크를 최신버전으로 세팅을 하고 서비스 클래스를 jUnit으로 작성해보았다...


Maven jUnit dependency
		
			junit
			junit
			4.7
			test
		


Maven Spring framework test
		
			org.springframework
			org.springframework.test
			3.2.3.RELEASE
		




AbstractApplicationContextTest.java
package com.openerp.service.test;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		"file:src/resources/applicationContext-datasource.xml", 
		"file:src/resources/applicationContext-transaction.xml", 
		"file:src/resources/applicationContext.xml"
			})
public class AbstractApplicationContextTest {
	@Autowired ApplicationContext ctx;
}

여기서 ContextConfiguration의 locations이 문제가 많다.
일반적인 설정으로 dataSource, transaction, AOP 설정등 모든걸 하나의 xml에 설정을 한다면 하나만 쓰면 되지만, 위 처럼 각 기능별(또는 업무별)로 설정을 나누어 버리면 위 처럼 순서를 맞추어 주어야 한다.
그리고 applicationContext 파일의 위치가 조금 달라지면 classpath를 쓸때 생각을 많이 해야 한다.


ApplicationContext.xml
	
	
		
	

	
    
	  
	  
	  
	  
	  
			
	  
	
	
	
		 
	

어노테이션으로 bean을 자동으로 찾도록 했다.
jUnit 에서도 이 어노테이션을 가져올 수 있도록 지원하기 때문에 일일이 bean으로 서비스 클래스(테스트 대상)를 등록할 필요는 없다.

기타 설정은 패스...

UserServiceTest.java
package com.openerp.service.test;

import java.util.Calendar;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import com.openerp.common.utils.JSONUtil;
import com.openerp.common.utils.Parameters;
import com.openerp.domain.User;
import com.openerp.service.UserService;

public class UserServiceTest extends AbstractApplicationContextTest{
	private static Log log = LogFactory.getLog(UserServiceTest.class);
	
	@Resource(name="userService")
	private UserService userService;
	    
	@Test
	public void testSelectAllUser(){
		// 아래 Parameters는 그냥 Map으로 보시면 됨..
		Parameters params = new Parameters();
		params.addValue("isDisable", "");
		List users = userService.listUsers(params);
		// 아래 코드는 JSON 형식으로 바꿔주는 로직.. 
		System.out.println(JSONUtil.toJSON(users, null));
	}
}

어노테이션으로 등록된 UserService를 가지고 오기 위해 @Resource 어노테이션을 이용했다.
applicationContext.xml에 bean 태그를 이용해서 등록한 bean은 @Autowired가 가능하나, 어노테이션 검색으로 찾은 bean은 안되더라.
@Resource를 이용해서 검색하게 하니 정상적으로 작동을 한다...
지금 이 클래스는 UserService에 대한 테스트 인데 다른 Service에 대한 테스트도 해야 한다면 지금과 같이 AbstractApplicationContextTest 만 상속해서 사용을 하면 된다.
이는 각 서비스가 사용하는 설정(applicationContext)가 동일할 경우이고, 만약 각 업무별로 applicationContext을 나누어 사용한다면 AbstractApplicationContextTest 외에 다른 클래스를 만들어 상속해서 쓰면 된다.
(결론은 설정을 안쪼갰다면 하나의 슈퍼클래스에 설정을 밀어넣고 상속해서 사용한다는 것..)


jUnit 등 Test Case를 적용할 경우 편하긴 편하다.
서버(tomcat 등)를 재시작 할 필요도 없고, 코드만 고치고 해당 @Test 어노테이션이 붙은 메서드를 jUnit으로 실행만 하면 기존 방법(서버를 통한)과 동일한 결과를 이루어 낼 수 있다.
java, xml 코드 하나 고치고 빌드 -> 서버 재시작 -> 웹에서 액션 -> 결과 확인 무한 반복...
이제 이런걸 안하고도 서비스 클래스를 쉽게 작성할 수 있으니 개발 효율성이 향상되는 효과를 누릴 수 있다.

 

Security 3.1.4를 사용하면서 LoginUrl 설정을 하면서 아래와 같은 워닝을 보았다.. 
	
		
		
	

확인해 본 결과 아래와 같이 변경 되었다..

org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint.setLoginFormUrl(String) 
          use constructor injection 

그러므로 아래와 같이 변경해준다..
	
		
		
	


아래는 deprecated 된 항목을 살펴볼 수 있는 URL.

http://static.springsource.org/spring-security/site/docs/3.1.x/apidocs/deprecated-list.html 
java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V

이런 에러가 나는데... SLF4J와 Spring Context 가 가지고 있는 slf4j 가 충돌이 나서 그런거므로 maven 설정을 아래와 같이..

 
		
			org.springframework
			org.springframework.spring-library
	    	libd
			3.2.3.RELEASE
			
		        
					javax.servlet
		            com.springsource.javax.servlet
				
		        
		        	org.springframework.context
		        	org.springframework
		        
		        
		        	
		        		org.springframework.context.support
		        	
		        	org.springframework
		        
			
		

		
			org.springframework
			spring-context
			3.2.3.RELEASE
			
                
                
                    commons-logging
                    commons-logging
                 
            
		
		
		
			org.springframework
			spring-context-support
			3.2.3.RELEASE
			
                
                
                    commons-logging
                    commons-logging
                 
            
		

GenericMyBatisDaoSupport.java
package com.openerp.common.dao;

import java.io.Serializable;
import java.util.ArrayList;

import org.apache.ibatis.exceptions.PersistenceException;

public interface GenericMyBatisDaoSupport{
	public T get(PK id) throws PersistenceException;//get obj of type T by the primary key 'id' 
	public ArrayList getAll() throws PersistenceException;//get all objects of type T
	public int insert(T objInstance) throws PersistenceException;//insert an object of type T into the database
	int update(T transientObject) throws PersistenceException; //update an object of type T    
	int delete(PK id)  throws PersistenceException;//delete an object of type T
}




AbstractGenericMyBatisDao.java
package com.openerp.common.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class AbstractGenericMyBatisDao implements GenericMyBatisDaoSupport {
	
	private static Logger log = LoggerFactory.getLogger(AbstractGenericMyBatisDao.class);
	private static final String NAMESPACE = "Mapper"; 

	@Autowired
	private SqlSessionFactory sqlSessionFactory; //reference to mybatis session factory 
	private Class type;

	/** 
	 * Define prefixes for easier naming convetions between XML mapper files and the DAO class 
	 **/
	public static final String PREFIX_SELECT_QUERY = "get";     //prefix of select queries in mapper files (eg. getAddressType) 
	public static final String PREFIX_INSERT_QUERY = "insert"; //prefix of insert queries in mapper files (eg. insertAddressType)
	public static final String PREFIX_UPDATE_QUERY = "update";  //prefix of update queries in mapper files (eg. updateAddressType)
	public static final String PREFIX_DELETE_QUERY = "delete";  //prefix of delete queries in mapper files (eg. deleteAddressType)
	public static final String PREFIX_LIST_QUERY = "list";  //prefix of delete queries in mapper files (eg. deleteAddressType)

	/** Default Constructor */
	public AbstractGenericMyBatisDao(Class type) {
		this.type = type;
	}
	
	/** Default Constructor */
	public AbstractGenericMyBatisDao(Class type, SqlSessionFactory sf) {
		this.type = type;
		this.sqlSessionFactory = sf;
		if(sf==null)
			log.error("Error: Could not instantiate MyBatisDAO. Loading myBatis sessionFactory failed.");  
	}

	/** Use this method to get a session factory for using in any methods impelmented in child dao classes */
	protected SqlSessionFactory getSessionFactory() {
		return sqlSessionFactory;
	}

	/** 
	 *  Default get by id method. 
	 *  

* Almost all objects in the db will * need this (except mapping tables for multiple joins, which you * probably shouldn't even have as objects in your model, since proper * MyBatis mappings can take care of that). *

* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <select id="getCarInfo" ... */ @SuppressWarnings("unchecked") public T get(PK id) throws PersistenceException { SqlSession session = sqlSessionFactory.openSession(); T obj = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_SELECT_QUERY+this.type.getSimpleName(); //If the object's calls name is AddressType.java, this matches the mapper query id: "namespace.getAddressType" obj = (T)session.selectOne(query,id); } finally { session.close(); } return obj; } /** * Method returns all rows for this object. *

* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <select id="getAllCarInfo" ... *

* SQL Executed: select * from [tablename] *

* Notes: *
* Consider overdiding this method in order to handle large numbers of objects * with multiple references. * LAZY LOADING should be enabled in this case, otherwise you might run out of memory (eg. get all UserAccounts if the table has 1,000,000 rows) * look into the aggresiveLazyLoading property * */ @SuppressWarnings("unchecked") public ArrayList getAll() throws PersistenceException { SqlSession session = sqlSessionFactory.openSession(); ArrayList list = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_SELECT_QUERY+"All"+this.type.getSimpleName(); list = (ArrayList)session.selectList(query); } finally { session.close(); } return list; } /** * Method returns first object which matches the given name (exact match). *

* It's up to you to decide what constitutes an object's name. Typically you would have a * NAME column in the table, but not all objects have this. Generally this method should be overriden (if you need it at all) * in the child dao class. *

* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <select id="getCarInfoByName" ... *

* SQL Executed (example): select * from [tablename] where NAME = ? * */ @SuppressWarnings("unchecked") public T getByName(String name) throws PersistenceException { SqlSession session = sqlSessionFactory.openSession(); T obj = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_SELECT_QUERY+this.type.getSimpleName()+"ByName"; obj = (T)session.selectOne(query, name); } finally { session.close(); } return obj; } /** * Method inserts the object into the table. *

* You will usually override this method, especially if you're inserting associated objects. *
* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <insert id="createCarInfo" ... *

* SQL Executed (example): insert into [tablename] (fieldname1,fieldname2,...) values(value1,value2...) ... * */ public int insert(T o) throws PersistenceException{ SqlSession session = sqlSessionFactory.openSession(); Integer status = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_INSERT_QUERY+o.getClass().getSimpleName(); status = (Integer)session.insert(query, o); session.commit(); } finally { session.close(); } return status; } /** * Method updates the object by id. *

* You will usually override this method. But it can be used for simple objects. *
* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <update id="updateCarInfo" ... *

* SQL Executed (example): update [tablename] set fieldname1 = value1 where id = #{id} * */ public int update(T o)throws PersistenceException { SqlSession session = sqlSessionFactory.openSession(); Integer status = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_UPDATE_QUERY+o.getClass().getSimpleName(); status = session.update(query, o); session.commit(); } finally { session.close(); } return status; } /** * Method deletes the object by id. *

* Example: *
* If your DAO object is called CarInfo.java, * the corresponding mapper query id should be: <delete id="deleteCarInfo" ... *

* SQL Executed (example): update [tablename] set fieldname1 = value1 where id = #{id} * */ public int delete(PK id) throws PersistenceException{ SqlSession session = sqlSessionFactory.openSession(); Integer status = null; try { String query = this.type.getSimpleName()+NAMESPACE+"."+PREFIX_DELETE_QUERY+this.type.getSimpleName(); status = session.delete(query, id); session.commit(); } finally { session.close(); } return status; } }


RoleService.java
package com.openerp.service;

import com.openerp.domain.Role;

public interface RoleService {
	Role getRole(int key);
}


RoleServiceImpl.java
package com.openerp.service.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.openerp.dao.RoleDao;
import com.openerp.domain.Role;
import com.openerp.service.RoleService;

@Service("roleMgr")
public class RoleServiceImpl implements RoleService{
	
	Log logger = LogFactory.getLog(this.getClass());
	
	@Autowired
	private RoleDao dao;

	@Override
	public Role getRole(int key) {
		return dao.get(key);
	}
	
}


Controller
package com.openerp.controller;

import java.io.IOException;

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

import net.arnx.jsonic.JSONException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.openerp.common.utils.JSONUtil;
import com.openerp.service.RoleService;

@Controller
public class JsonController {

	@Autowired private RoleService roleMgr;
	
	@RequestMapping("/json/sampleData.do")
	public void sendJsonData(HttpServletRequest req, HttpServletResponse rep){
		int key = Integer.parseInt(req.getParameter("key"));
		try {
			rep.setCharacterEncoding("UTF-8");
			rep.getWriter().write(JSONUtil.toJSON(roleMgr.getRole(key), null));
			
		} catch (JSONException e) {
			e.printStackTrace();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


SqlMapConfig.xml



	
		 
	

	
		
	

	



mapUnderscoreToCamelCase 이 속성을 안적어주면 아래 필드가 매핑되지 않는다.
ROLE_NAME_KOR 이런 필드는 카멜 표기법으로 표기하지 않기 때문에 꼭 위의 속성이 필요하다...


SQL File :

Full Source :
설명 적는게 너무 귀찮으다...


Mapper 방식은 아래 처럼 사용한다.
	
	    
	    
	


pacakge com.openerp.dao

public interface RoleDao{
	List listRole(Parameters params);
	List listRole(Parameters params, int pg, int ps);
}

이 경우 Mapper에 등록을 했기때문에 roleMapper 라는 이름으로  bean에 등록이 된다.

"예전부터 사용해오던 DAO 패턴을 같이 쓸 수 없을까?" 라는 궁금증이 생겨 반나절동안 해 보았는데... 안된다.

DAO 패턴도 어쨋든 Spring DI에 등록이 되어야 하는데 같은 클래스가 두개의 이름으로 등록이 되는 현상이 발생된다. 

No qualifying bean of type [com.openerp.dao.RoleDao] is defined: expected single matching bean but found 2: roleDao,roleMapper


이 같은 결론을 얻고 생각을 해보니.. 

DAO Pattern을 그냥 간소화 시켜서 사용하는게 Mapper 방식이네? 


reference)
myBatis DAO Pattern example : http://blog.idleworx.com/2011/09/mybatis-dao-example-code-tutorial.html 

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

MyBatis 3.2) Mapper 방식과 DAO Pattern은 같이 사용할 수 없다.  (0) 2013.07.19
프로시저 호출 시 주의 할 점..  (0) 2012.11.13
iBatis isEqual  (0) 2012.11.05
[3.0] forEach 사용  (0) 2010.07.15
[3.0] like 검색.  (0) 2010.07.09
[3.0] JDBC Type  (0) 2010.07.09

기존에 사용하던 Spring 3.1에서 3.2.3으로 변경했다. 하는김에 myBatis도 최신으로 변경.. 
이러다가 그냥 back-end를 다 최신으로 변경하기로 작정하고 작업 진행..

applicationContext.xml 에서 myBatis 설정이 조금 변경되었다. 

org.springframework.orm.ibatis.support.SqlMapClientDaoSupport
as of Spring 3.2, in favor of the native Spring support in the Mybatis follow-up project (http://code.google.com/p/mybatis/)
org.springframework.orm.ibatis.SqlMapClientFactoryBean
as of Spring 3.2, in favor of the native Spring support in the Mybatis follow-up project (http://code.google.com/p/mybatis/)
org.springframework.orm.ibatis.SqlMapClientTemplate
as of Spring 3.2, in favor of the native Spring support in the Mybatis follow-up project (http://code.google.com/p/mybatis/)

3.2 버전 이전은 위 클래스를 이용하여 myBatis 설정을 했지만 3.2 이후부터는 불가능하다.

> myBatis configuration codes in applicationContext
	
	
		
		
		
		
		
			
		
	

심플하다... SqlMapConfig에 mapper를 일일이 적어주어도 되지만
mapperLocations를 이용하여 패키지 검색으로 자동 매핑되게도 할 수 있고 이게 더 편하다.

또 하나, 기본 설정 그대로 사용하고자 한다면 configLocation을 적지 않아도 된다.

아래는 maven에서의 myBatis 설정에 관련된 코드.
	
		
		3.2.2
		1.2.0
		
	

	
		
		
			org.mybatis
			mybatis
			${org.mybatis.version}
		
		
			org.mybatis
			mybatis-spring
			${org.mybatis.spring.version}
		
	




 

 


Spring은 자체 maven Repository가 존재한다. 
이곳에서 라이브러리를 통으로 내려 받을 수 있다. 

site : https://ebr.springsource.com/repository/app/ 
	
		
		    com.springsource.repository.bundles.release
		    SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases
		    http://repository.springsource.com/maven/bundles/release
		
		
		
		    com.springsource.repository.bundles.external
		    SpringSource Enterprise Bundle Repository - External Bundle Releases
		    http://repository.springsource.com/maven/bundles/external
		
		
		
		    com.springsource.repository.libraries.release
		    SpringSource Enterprise Bundle Repository - SpringSource Library Releases
		    http://repository.springsource.com/maven/libraries/release
		
		
		
		    com.springsource.repository.libraries.external
		    SpringSource Enterprise Bundle Repository - External Library Releases
		    http://repository.springsource.com/maven/libraries/external
		
	

	
		
			org.springframework
			org.springframework.spring-library
	    	libd
			3.2.3.RELEASE
			
		        
					javax.servlet
		            com.springsource.javax.servlet
				
			
		
	


junit으로 테스트를 진행하고자 할 경우 아래 의존성도 추가해 주어야 한다.
		
			org.springframework
			org.springframework.test
			3.2.3.RELEASE
		

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

myBatis DAO Pattern example  (0) 2013.07.19
Spring 3.2.3 + myBatis 3.2.2  (0) 2013.07.19
Spring Framework 3.2.3 Maven으로 한번에 추가하기..  (0) 2013.07.18
Spring 3.2.2 + Tiles 3.0  (0) 2013.05.22
Spring Security 설정.  (2) 2011.08.30
[2011-05-26] Spring Security 설정 오류.  (0) 2011.05.26

+ Recent posts