스프링에서 몽고db를 연동하는 방법을 찾아보면, 스프링 boot랑 관련된 내용은 꽤 많이 나오는데..스프링과 관련된 내용은 그다지 많지 않는 것 같다.
몽고db와의 연동은 하이버네이트를 사용해 보았거나 JPA방식의 연동을 해 본 경험이 있으면 그다지 어렵지가 않다.
아무튼, 몽고db와의 연동을 위해서는 라이브러리를 받아야 한다. pom.xml에 라이브러리를 등록하여 주자.
라이브러리 버전때문에 문제가 발생 할 수 있으니 의존성관련 문제가 발생하면 구글링을 통해 맞는 버전을 찾자.
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.9.1.RELEASE</version>
</dependency>
위 2개의 라이브러리가 추가 한 뒤에 설정파일(xml)을 만들어주자. servlet.xml 같은 파일에 그냥 추가하여도 된다.
Namespace가 추가되야 하므로 유의하자
<?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:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.8.xsd">
<!-- mongo -->
<mongo:mongo-client
host="주소"
port="포트" credentials="사용자아이디:비밀번호@컬렉션이름" >
<mongo:client-options
connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="1000"
max-wait-time="1500"
socket-keep-alive="false"
socket-timeout="1500"
/>
</mongo:mongo-client>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongo" />
<constructor-arg name="databaseName" value="컬렉션 이름" />
</bean>
</beans>
해당파일은 resources에 context-mongo.xml 로 저장하였다.
여기서 credentials 라는 부분이 있는데, 반드시 "사용자아이디:비밀번호@컬렉션이름" 형식으로 입력해야 된다.
예) admin:1234@test_collection
해당 파일이 스프링이 로드되면서 읽혀지도록 web.xml에 추가를 하여 준다.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:내가만든resources경로/context-mongo.xml</param-value>
</context-param>
여기까지 하였고, 서버를 구동하였을 때 아무런 문제가 없다면 일단 1차 목표는 달성 한 것이다.
다음으로 실제 사용방법이다. 여기서는 MongoRepository 를 사용한 방법이 아니라 MongoTemplate를 사용한 내용이다. (lombok은 사용하지 않았다)
setter, getter를 위한 클래스를 만든다. 패키지명은 따로 포함하지 않았다.
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document //매핑을 위한 가이드 역할
public class MongoPojo {
@Id //요것이 고유 키 값
private String id;
private int number; //숫자를 매핑
private String text; //문자를 매핑
private Object all; //모든 객체를 매핑
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Object getAll() {
return all;
}
public void setAll(Object all) {
this.all = all;
}
}
다음으로 비지니스로직을 수행 할 서비스 클래스를 만들어준다. 패키지명은 따로 포함하지 않았다. 마찬가지로 위에 만든 클래스도 Import하지 않았다.
import java.util.HashMap;
import java.util.List;
import javax.annotation.Resource;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
@Service("MongoService")
public class MongoService {
private String dbName = "컬렉션이름";
@Autowired
private MongoTemplate mongodb; //몽고Template
//단일정보 가져오기
@Override
public MongoPojo getInfor(MongoPojo vo) {
Query query = new Query(Criteria.where("_id").lt(new ObjectId("5cd276f2d079c73e93149837")));
MongoPojo obj = mongodb.findOne(query, MongoPojo.class, dbName);
return obj;
}
//리스트형태 데이터 전부 가져오기
public List<MongoPojo> getList(MongoPojo vo) {
Query query = new Query(Criteria.where("number").gte(123)); //조건1
query.addCriteria(Criteria.where("text").regex("검색문자조건")); //조건2
return mongodb.find(query, MongoPojo.class, dbName);
}
//페이징 처리가 포함된 리스트
public HashMap<Object,Object> getPageList(MongoPojo vo, int startNum, int endNum) {
final HashMap<Object,Object> result = new HashMap<Object,Object>(2);
Query query = new Query(Criteria.where("number").gte(123)); //조건
Pageable pageable = new PageRequest(startNum, endNum);
query.with(pageable); //페이지 정보를 포함
List<MongoPojo> obj = mongodb.find(query, MongoPojo.class, dbName);
long total = mongodb.count(query, MongoPojo.class, dbName);
Page<MongoPojo> list = new PageImpl<MongoPojo>(obj, pageable, total);
result.put("total",total);
result.put("list",list);
return result;
}
//등록
public String insertData(MongoPojo vo) throws Exception {
mongodb.insert(vo, dbName); //vo객체에 정보를 토대로 알아서 등록한다.
return vo.getId(); //id가 등록된 고유 키 값이다. 등록이 성공하면 vo에 담아준다.
}
//수정
public void updateData(MongoPojo vo) throws Exception {
mongodb.save(vo, dbName); //vo객체의 정보를 토대로 알아서 매칭하여 수정한다.
//update 이름으로 시작하는 메소드가 훨씬 더 사용하기 편리하다.
}
//삭제
public void deleteData(MongoPojo vo) throws Exception {
mongodb.remove(vo, dbName); //vo객체의 정보를 토대로 알아서 삭제한다.
}
}
위 내용이 끝이다. 물론 단순하게 기능을 나열하였기에 복잡한 조회나 기능이 추가되면 수정이 필요하다.
데이터 조회에 대해서 살펴볼 부분이 있는데.. 여기서 Query라는 클래스가 있다.
Query라는 클래스는 말 그대로 조회를 위한 조건의 집합이다.
Query라는 클래스는 생성자로 Criteria(한글 뜻 : 기준)는 클래스를 가질수 있거나 아니면 addCriteria라는 메소드로 Criteria 클래스를 받을 수 있다.
Criteria는 where 이라는 메소드로 조건을 받으며, 이후 gte, regex라는 메소드 등을 활용하여 조건에 대한 값을 추가하여 조건이 이어 질 수 있다. (빌더패턴)
* 이러한 의미이다 : Criteria.조건.내용
where에는 내가 주고 싶은 조건을 입력하고 이후 반드시 해당 내용을 지정하는 메소드를 호출해야 하는데, 위 예제는 gte와 regex만 사용하였다. 기타 나머지 자주 사용되는 메소드는 아래와 같다.
메소드 명칭 | 의미 |
gt | 주어진 값보다 더 큰 |
gte | 주어진 값보다 크거나 같은 |
lt | 주어진 값보다 더 작은 (일반 조회에서는 equals로도 사용된다) |
lte | 주어진 값보다 작거나 같은 |
ne | 주어진 값과 동일하지 않은 |
in | 주어진 배열에 속하는 어떤 |
nin | 주어진 배열에 속하지 않는 어떤 |
regex | 문자를 포함하고 있는 |
getInfor라는 메소드는 1개의 대상을 조회하는 메소드인데, 여기서 id를 조회하는 경우에 new ObjectId를 사용한 점이 흥미롭다. vo객체 그 자체를 주어도 사실 상관 없다.
또한 친절하게도 Pageable이라는 인터페이스를 통해서 페이징도 쉽게 지원한다.
Query클래스에 with라는 메소드를 통해서 구현한 Pageable을 추가하면 된다. 사용법은 getPageList 메소드를 참조하자.
* 추가(에노테이션 설정방법, 터널링 방법)
xml에 등록하지 않고 에노테이션으로 하는 방법이다.
에노테이션으로는 컴포넌트 에노테이션을 사용 하였다.
import javax.annotation.PostConstruct;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
@Component
public class SSHWithMongoTemplate {
private static String MONGO_DB_NAME ;
private static String MONGO_USER ;
private static String MONGO_PWD ;
private static Integer MONGO_PORT ;
private static MongoTemplate DB;
@PostConstruct //빈 객체가 구성된 이후에 동작하도록 한다.
public void onInit() {
MONGO_DB_NAME = "DB이름";
MONGO_PORT = 27017;
MONGO_USER = "사용자아이디";
MONGO_PWD = "비번";
Init();
}
//null인경우
private void Init() {
if(DB == null){
try {
String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+MONGO_PORT + "/" + MONGO_DB_NAME;
MongoClient mongoClient = new MongoClient(new MongoClientURI(combineUrl));
DB = new MongoTemplate(mongoClient, MONGO_DB_NAME);
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("template is not empty");
}
}
public MongoTemplate getDB(){
if(DB == null){
Init();
}
return DB;
}
}
해당 컴포넌트를 생성하면 서비스나 컨트롤러에서 사용하는방법은 다소 번거롭더라도 아래처럼 바꾸어야 한다.
//임포트 생략..
@Service("MyService")
public class MyServiceImpl extends EgovAbstractServiceImpl implements MyService{
@Autowired
private SSHWithMongoTemplate template; //앞서 만든 몽고db 컴포넌트
private MongoTemplate mongodb; //실제 사용할 몽고템플릿
@PostConstruct //마찬가지로 빈객체 생성 이후에 몽고템플릿 기능을 살려주도록 한다.
public void init() {
mongodb = template.getDB(); //만들어진 몽고db 컴포넌트에서 템플릿 객체를 가져오는 부분.
}
//이하 비지니스 로직..
}
그렇다면 이를 응용하면 터널링을 통한 몽고db 접속도 가능 해 진다.
몽고DB에 직접 접속 할 수 없으면서 SSH를 사용 해서 접속을 해야만 하는 상황이라면 위 방식이 효율적이다.
터널링을 위해서는 SSH 접속을 도와주는 라이브러리가 필요 하다.
JSch 라이브러리를 설정하도록 하자.
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
이후 단계로는,
1. ssh 에 접속 한다.
2. 접속이 완료되면 포트 포워딩을 실시 한다.
3. 데이터 베이스에 접속한다.
- 데이터베이스에 접속 할 때는 포트포워딩한 가상 포트를 부여하도록 한다.
- 즉, 내가 접속해야될 데이터베이스 포트가 27017이라면 27017로 접속하는 것이 아니라 임의로 설정한 포트를 사용 한다.
- 아래 완성된 소스코드 가운데 부분에 setPortForwardingL() 메소드를 참조하자.
아래 완성된 소스코드이다.
import javax.annotation.PostConstruct;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
@Component
public class SSHWithMongoTemplate {
//ssh관련 설정 값들 ---------- ---------- ----------
private static String SSH_USER = "";
private static String SSH_PASSWORD = "";
private static String SSH_HOST = "";
private static Integer SSH_PORT = 1;
//몽고DB관련 설정 값들 ---------- ---------- ----------
private static String MONGO_DB_NAME ;
private static String MONGO_USER ;
private static String MONGO_PWD ;
private static Integer MONGO_PORT ;
private static Session SSH_SESSION; //SSH 세션
private static MongoTemplate DB; //몽고템플릿 객체
@PostConstruct
public void onInit() {
MONGO_DB_NAME = "데이터베이스이름";
MONGO_PORT = 27017;
MONGO_USER = "데이터베이스 사용자 이름";
MONGO_PWD = "데이터베이스 비밀번호";
SSH_USER = "SSh 사용자 이름";
SSH_HOST = "SSh 주소";
SSH_PASSWORD = "SSh 비밀번호";
SSH_PORT = 22; //SSh 포트번호
Init();
}
private void Init() {
if(DB == null){
try {
//ssh 접속부분 -----------------
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
SSH_SESSION = null;
SSH_SESSION = jsch.getSession(SSH_USER, SSH_HOST, SSH_PORT);
SSH_SESSION.setPassword(SSH_PASSWORD);
SSH_SESSION.setConfig(config);
SSH_SESSION.connect();
//ssh 접속 이후 포트 포워딩 부분------------------
final int virtualInt = 23192; //가상포트 23192를 부여한다. 포트충돌 방지
//여기가 핵심, 몽고 포트는 27017을 사용 할 지라도 가상포트를 사용해서 몽고DB에 접속한다.
SSH_SESSION.setPortForwardingL(virtualInt, "127.0.0.1", MONGO_PORT);
String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;
MongoClient mongoClient = new MongoClient(new MongoClientURI(combineUrl));
DB = new MongoTemplate(mongoClient, MONGO_DB_NAME);
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("template is not empty");
}
}
public MongoTemplate getDB(){
if(DB == null){
Init();
}
return DB;
}
}
23192라는 포트 번호는 마음데로 주어도 상관 없는데 27017이나 22, 23 등 기존에 사용중인 포트번호는 피하자.
만약 id와 password가 없다면 하단에 보이는 아래부분을 고쳐주어야 한다.
//변경 전
String combineUrl = "mongodb://"+MONGO_USER + ":" + MONGO_PWD + "@127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;
//변경 후
String combineUrl = "mongodb://127.0.0.1:"+virtualInt + "/" + MONGO_DB_NAME;
* 스프링 웹 리스너를 활용한 터널링 방법
https://lts0606.tistory.com/278
'몽고DB > Java 몽고DB' 카테고리의 다른 글
몽고DB 트랜잭션을 위한 리플리카 셋, 적용 테스트(Mongodb transaction, Mongodb replica set) (5) | 2019.11.26 |
---|---|
Java Mongodb 연동, Java 몽고db 연동 (4.0이상 버전) (0) | 2019.11.26 |
MongoTemplate Aggregate 2 (lookup, unwind,first,last,push ..) (0) | 2019.09.06 |
MongoTemplate Aggregate 사용간 파싱 버그 (0) | 2019.07.19 |
MongoTemplate Aggregate (0) | 2019.06.21 |
댓글