RealGrid2 Basic with JAVA Spring & MyBatis & MARIADB
Architectures
- 개발도구: Spring Tool Suite 3.9.11
- WAS: tomcat 8.5
- 데이터베이스: MariaDB 10.6.8
Introduction
본 가이드는 Java Spring MVC와 MariaDB환경에서 RealGrid에 데이터를 뿌려주는 간단한 화면을 만드는 방법에 대해 설명하고 있습니다. 본 자습서를 읽고 내용을 이해 하기 위해서는 Java에 대한 기초적인 지식이 필요합니다. 본 예제에서는 Spring Tool Suite 3.9.11에 Spring MVC 만 사용하였습니다. 본 가이드는 MariaDB의 특정 테이블에 들어 있는 데이터를 RealGrid2가 포함된 웹화면에서 조회, 추가, 수정, 삭제기능을 구현하고 있습니다.
A New Web Suite
Spring Tool Suite 3.9.11을 실행합니다. File(파일)메뉴에서 New -> Spring Legacy Project ->Spring MVC Project메뉴를 실행 합니다. Project name, Top Level Package를 설정해주고 새 프로젝트를 생성 합니다.본 예제에서는 Project name을 realGrid, Top Level Package는 com.java.realgrid로 생성하겠습니다.
Create a Web Page
RealGrid를 장착할 새로운 웹 페이지를 생성합니다. 새로운 웹 페이지의 이름은 home.jsp로 합니다.
Setup the RealGrid
RealGrid를 설치하는 작업은 어떤 개발환경이나 실행환경에서도 간단히 처리할 수 있습니다. 정식으로 제품을 구매한 경우와 평가판으로 제품을 받은 경우 모두 동일한 방법으로 설치를진행 할 수 있습니다. 공급받은 제품에 포함된 js파일과 css파일을 resources의 하위 폴더로 옮겨줍니다.
Initialization
GridView와 DataProvider를 매핑하기 위한 변수를 선언합니다. setDataSource를 호출하여 GridView에 DataProvider를 연결합니다.
Add Fields and columns
RealGrid는 구조적으로 데이터 영역과 뷰 영역이 분리 되어 있습니다. Column은 데이터 영역에서 Field를 표현하는 ViewModel이므로 그리드 위에 Field를 표현하기 위해서는 Column에 대한 정의가 필요합니다.
DataProvider의 setFields()로 Field를 정의합니다.
var fields = [
{
fieldName: "code",
},
{
fieldName: "product_name",
},
{
fieldName: "volume",
},
{
fieldName: "unit",
},
{
fieldName: "price",
dataType: "number",
},
];
var columns = [
{
fieldName: "code",
width: 180,
header: {
text: "코드",
},
},
{
fieldName: "product_name",
width: 180,
header: {
text: "제품명",
},
},
{
fieldName: "volume",
width: 180,
header: {
text: "용량",
},
},
{
fieldName: "unit",
width: 180,
header: {
text: "단위",
},
},
{
fieldName: "price",
width: 180,
header: {
text: "단가",
},
},
];
var dataProvider, gridContainer, gridView;
function createGrid(container) {
$("#realgrid").css("width", "50%");
$("#realgrid").css("height", "550px");
dataProvider = new RealGrid.LocalDataProvider();
gridView = new RealGrid.GridView(container);
gridView.setDataSource(dataProvider);
dataProvider.setFields(fields);
gridView.setColumns(columns);
}
function start() {
createGrid("realgrid");
}
window.onload = start;
이제 웹 페이지를 실행해보면 그리드에 컬럼이 추가된 모습을 확인할 수 있습니다.
Connecting a Database
이제 서버 작업으로 잠시 눈을 돌려 보겠습니다. Java Spring에서 MariaDB에 연결하여 데이터를 가져오는 작업은 어렵지 않습니다.
먼저 DB연결이 되었는지, DB에서 올바른 데이터를 가지고 오는지 확인을 하기위해 home.jsp페이지를 잠시 수정하겠습니다. DB연결 테스트용으로 HomeController도 수정합니다.
<html lang="=ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1,maximum-scale=1"
/>
<title>DB TEST</title>
</head>
<body>
<div class="title">
<span>DB TEST</span>
</div>
<span>${data.get(0).getProduct_name()}</span>
</body>
</html>
@RequestMapping(value = "/", method = RequestMethod.GET)
public String dbtest(Locale locale, Model model) {
List<RealGridDemoVO> data = realGridDemoService.getDatas();
System.out.println(data.get(0).getProduct_name());
model.addAttribute("data", data);
return "home";
}
WEB-INF에 있는 web.xml에 UTF-8 encodingFilter를 추가로 설정합니다.
<!-- 스프링 인코딩 필터 등록 시작 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
pom.xml에 mariadb, mybatis관련 dependency를 추가합니다.
<!-- RealGrid Demo를 위한 dependencies -->
<!-- 3. DB연결 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 9. mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 6. DBCP 설정 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.1</version>
</dependency>
<!-- 마리아디비 -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.3.0</version>
</dependency>
<!-- JSON 처리를 위한 dependency -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.3</version>
</dependency>
DB 연결을 위한 MVC방식의 클래스를 구성합니다. src/main/java폴더 안에 service, dao, vo를 각각 생성합니다.
@Service
RealGridDemoService.java
서비스 계층에서 사용할 RealGridDemoService를 만듭니다.
@Service
public class RealGridDemoService {
@Autowired
RealGridDemoDAO realGridDemoDAO;
public List<RealGridDemoVO> getDatas() {
return realGridDemoDAO.getDatas();
}
}
RealGridDemoDAO.java
퍼시스턴스 계층에 사용할 interface RealGridDemoDAO를 생성합니다.
public interface RealGridDemoDAO {
public List<RealGridDemoVO> getDatas();
}
RealGridDemoVO.java
DB정보를 받고 넘겨줄 RealGridDemoVO를 생성합니다.
public class RealGridDemoVO {
private String code;
private String product_name;
private String volume;
private String unit;
private int price;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getProduct_name() {
return product_name;
}
public void setProduct_name(String product_name) {
this.product_name = product_name;
}
public String getVolume() {
return volume;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
이미 생성되어 있는 root-context.xml에 mybatis와 db.properties 설정을 해줍니다.
<!-- 프로퍼티파일 사용 등록 -->
<context:property-placeholder
location="classpath:db.properties" />
<!-- dataSource 설정 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${m.driver}" />
<property name="url" value="${m.url}" />
<property name="username" value="${m.username}" />
<property name="password" value="${m.password}" />
</bean>
<!-- mybatis추가 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 아래부분은 매핑할 xml파일이 있는 패키지경로를 설정한다. -->
<property name="typeAliasesPackage"
value="com.java.realgrid.vo" />
<!-- vo.java가 위치할 경로 -->
<property name="mapperLocations"
value="classpath*:/mapper/*.xml" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.java.realgrid.dao" />
<!-- dao.java 가 위치한 경로 -->
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory" />
</bean>
<!-- 트랜젝션 관리자 추가 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
controller와 service를 servlet-context.xml에 추가해줍니다.
<beans:bean id="RealGridDemoService"
class="com.java.realgrid.service.RealGridDemoService" />
<!-- service가 위치한 경로 -->
<context:component-scan
base-package="com.java.realgrid.controller" />
mariaDB설정을 위한 db.properties 파일을 src/main/resources 폴더 아래에 넣어줍니다.
m.driver=org.mariadb.jdbc.Driver
m.url=jdbc:mariadb://localhost:3306/test?allowMultiQueries=true
m.username=root
m.password=1234
추가로 mapper 폴더와 쿼리를 작성할 sample.xml파일을 생성해줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java.realgrid.dao.RealGridDemoDAO">
<!-- namespace에 만들어둔 DAO의 경로와 해당 파일의 이름까지 작성합니다. -->
<select id="getDatas" resultType="com.java.realgrid.vo.RealGridDemoVO">
SELECT * FROM sample;
</select>
</mapper>
윈도우 브라우저에 http://localhost:8080/realgrid/ (opens in a new tab) 를 입력하면 콘솔창과 윈도우 브라우저에서 DB 데이터를 가지고 온 것을 확인할 수 있습니다.
확인을 완료했으니 다시 home.jsp에 그리드 영역을 생성합니다.
<html lang="=ko">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1,maximum-scale=1"
/>
<title>Home</title>
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"
></script>
<script src=" ${pageContext.request.contextPath }/resources/js/realgrid/realgrid-lic.js"></script>
<script src=" ${pageContext.request.contextPath }/resources/js/realgrid/realgrid.2.x.x.min.js"></script>
<script src=" ${pageContext.request.contextPath }/resources/js/realgrid/libs/jszip.min.js"></script>
<link
rel="stylesheet"
href="${pageContext.request.contextPath }/resources/css/realgrid-style.css"
/>
</head>
<body>
<script>
// 그리드 관련 코드를 넣을 script
</script>
<div class="title">
<span>RealGrid on Java Spring MVC and MaridDB</span>
</div>
<div id="realgrid"></div>
</body>
</html>
Load Data
RealGrid에 데이터를 설정하는 방법은 여러가지가 있습니다. 본 자습서에서는 DataProvider의 FillJsonData()를 이용해 비동기 방식으로 데이터를 가져오는 방법에 대해 설명합니다. 이전 장에서 만든 서버 페이지를 호출하여 데이터를 가져온 다음 이를 DataProvider에 넘겨주기 위해 아래 코드를 입력합니다.
function loadData(provider) {
$.ajax({
type: "post",
dataType: "json",
url: "getDatas",
success: function (data) {
dataProvider.fillJsonData(data);
},
error: function (xhr, status, error) {
alert(error);
},
});
}
Controller , Service, DAO 를 작성합니다.
HomeController
@RequestMapping(value = "getDatas", method = RequestMethod.POST)
public @ResponseBody List<RealGridDemoVO> productlList() {
List<RealGridDemoVO> data = realGridDemoService.getDatas();
return data;
}
Service
@Service
public class RealGridDemoService {
@Autowired
RealGridDemoDAO realGridDemoDAO;
public List<RealGridDemoVO> getDatas() {
return realGridDemoDAO.getDatas();
}
}
DAO
@Repository
public interface RealGridDemoDAO {
public List<RealGridDemoVO> getDatas();
}
다시 한번 createGrid에 loadData()함수를 호출하여 데이터를 RealGrid에 로드해 보도록 하겠습니다.
var fields = [
{
fieldName: "code",
},
{
fieldName: "product_name",
},
{
fieldName: "volume",
},
{
fieldName: "unit",
},
{
fieldName: "price",
dataType: "number",
},
];
var columns = [
{
fieldName: "code",
width: 180,
header: {
text: "코드",
},
},
{
fieldName: "product_name",
width: 180,
header: {
text: "제품명",
},
},
{
fieldName: "volume",
width: 180,
header: {
text: "용량",
},
},
{
fieldName: "unit",
width: 180,
header: {
text: "단위",
},
},
{
fieldName: "price",
width: 180,
header: {
text: "단가",
},
},
];
var dataProvider, gridContainer, gridView;
function createGrid(container) {
$("realgrid").css("width", "50%");
$("#realgrid").css("height", "550px");
$("#realgrid").css("margin", "0 auto");
dataProvider = new RealGrid.LocalDataProvider();
gridView = new RealGrid.GridView(container);
gridView.setDataSource(dataProvider);
dataProvider.setFields(fields);
gridView.setColumns(columns);
loadData(dataProvider);
}
Insert Data
그리드의 editOptions.insertable이 true이면 사용자는 Insert키로 행 삽입을 시작할 수 있고 editOptions.appendable이 true이면 마지막 행에서 아래 화살표 키로 행 추가를 시작할 수 있습니다. 셀 편집 중 Esc 키를 누르면 셀 편집이 취소되고, 셀 편집 중이 아닌 상태에서 Esc 키를 누르면 행 추가가 취소됩니다. 또, 아래 예제와 같이 javascript 메소드를 통해 사용자가 행 삽입/추가를 할 수 있습니다.
editOptions에 대한 보다 자세한 설명은 다음 링크를 참조하세요.
https://docs.realgrid.com/refs/Interface/EditOptions#editoptions (opens in a new tab)
function createGrid(container) {
$("#realgrid").css("width", "50%");
$("#realgrid").css("height", "550px");
dataProvider = new RealGrid.LocalDataProvider();
gridView = new RealGrid.GridView(container);
gridView.setDataSource(dataProvider);
dataProvider.setFields(fields);
gridView.setColumns(columns);
setOptions(gridView);
loadData(dataProvider);
}
function setOptions(grid) {
grid.setOptions({
edit: {
insertable: true,
appendable: true,
},
});
}
이 예제에서는 바로 행 저장하는 방식으로 처리하도록 하겠습니다. 우선 서버에서 인서트 작업을 처리 하기 위해 controller에 insert 메서드를 추가하도록 하겠습니다.
insert 메서드의 내용은 다음과 같습니다.
@RequestMapping(value = "addData")
public @ResponseBody int addData(@ModelAttribute RealGridDemoVO data) {
int num = realGridDemoService.addData(data);
return num;
}
home.jsp에서 다음과 같이 삽입/추가/저장 버튼 3개를 추가합니다.
<div class="save_btn_container">
<button onClick="btnSaveData()">현재 행 저장</button>
</div>
<div id="realgrid"></div>
<div class="toolbar">
<button onClick="btnGridInsert()">그리드의 beginInsertRow</button>
<button onClick="btnGridAppend()">그리드의 beginAppendRow</button>
</div>
각 버튼이 수행할 메소드를 작성합니다.
function btnGridInsert() {
var curr = gridView.getCurrent();
gridView.beginInsertRow(Math.max(0, curr.itemIndex));
gridView.showEditor();
gridView.setFocus();
}
function btnGridAppend() {
gridView.beginAppendRow();
gridView.showEditor();
gridView.setFocus();
}
function btnSaveData() {
gridView.commit();
var currRow = gridView.getCurrent().dataRow;
if (currRow < 0) return;
var currState = dataProvider.getRowState(currRow);
if (currState == "created") {
saveData("addData");
}
}
function saveData(urlStr) {
var jRowData = dataProvider.getJsonRow(gridView.getCurrent().dataRow);
$.post(urlStr, jRowData, function (data) {
if (data > 0) {
alert("저장 성공");
dataProvider.clearRowStates(true);
} else {
alert("저장 실패!");
}
});
}
웹 페이지를 실행하여, 인서트 후 저장버튼을 클릭하여 정상적으로 Insert가 되는지 확인 합니다. 단, 저장 버튼은 현재 선택된 행에만 작동합니다.
RealGrid는 dataProvider를 통하여 데이터 입출력을 행합니다. 그리드에서 편집된 데이터는 그리드에만 존재하며 편집 완료시에만 dataProvider로 반영됩니다.
편집 완료는 행 바꿈이나 RealGrids.commit() 메소드를 호출 하여 편집 완료를 시킬 수 있습니다.
행 추가후 인서트할 자료를 입력 한 후 Row를 변경(commit())하여 stateBar 영역이 어떻게 바뀌었는지 확인해보세요
RealGrid 의 RowStates
RealGrid 의 RowState는 5가지의 상태가 있으며 NONE, CREATED, UPDATED, DELETED, CREATE_AND_DELETED로 구분됩니다.
Update Data
Update는 Insert의 작업방법과 같습니다. 별도의 버튼이 필요하지 않고 저장시에만 이 데이터가 수정되었는지 확인하면 됩니다. RealGrid는 데이터가 수정되면 RowState를 UPDATED로 변경합니다. 이것을 토대로 home.jsp와 Controller를 작성해보겠습니다.
@RequestMapping(value = "updateData")
public @ResponseBody int updateData(@ModelAttribute RealGridDemoVO data) {
int num = realGridDemoService.updateData(data);
return num;
}
function btnSaveData() {
gridView.commit();
var currRow = gridView.getCurrent().dataRow;
if (currRow < 0) return;
var currState = dataProvider.getRowState(currRow);
if (currState == "created") {
saveData("addData");
} else if (currState == "updated") {
saveData("updateData");
}
}
위의 코드와 같이 현재 그리드 상태가 UPDATED인지 확인하고 updateData를 호출하도록 수정하게 됩니다.
마찬가지로 mapper안의 sample.xml, DAO, Service에도 추가해줍니다.
<update id="updateData"
parameterType="com.java.realgrid.vo.RealGridDemoVO">
UPDATE sample
SET
product_name = #{product_name},
volume = #{volume},
unit = #{unit},
price = #{price}
WHERE code = #{code}
</update>
RealGridDemoDAO.java
public interface RealGridDemoDAO {
public List<RealGridDemoVO> getDatas();
public int addData(RealGridDemoVO realGridDemoVO);
public int updateData(RealGridDemoVO realGridDemoVO);
}
RealGridDemoService.java
@Service
public class RealGridDemoService {
@Autowired
RealGridDemoDAO realGridDemoDAO;
public List<RealGridDemoVO> getDatas() {
return realGridDemoDAO.getDatas();
}
public int addData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.addData(realGridDemoVO);
}
public int updateData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.updateData(realGridDemoVO);
}
}
Delete Data
그리드 editOptions.deletable이 true이면 사용자는 Ctrl+Del 키를 눌러 현재 선택된 행을 삭제할 수 있습니다. 실제 삭제하기 전에 사용자 확인을 받을 필요가 있다면 editOptions.confirmWhenDelete 속성을 true로 지정하면 됩니다. 확인 메시지를 editOptions.deleteRowsMessage로 지정할 수도 있습니다.
이 예제에서는 삭제 기능 수행시 바로 삭제하는 것이 아닌 RowState를 DELETED 나 CREATE_AND_DELETED로 변경하는 softDeleting 옵션을 사용합니다.
물론 삭제 기능 사용시 행을 바로 삭제하는 것도 RealGrid 에서 제공되는 callback 함수인 dataProvider.onRowDeleting() 이나 dataProvider.onRowDeleted() 를 이용하여 처리 할 수 있습니다.
이제 실제 작업을 해보도록 하겠습니다. INSERT, UPDATE DATA에서 작업했던것과 같은 방법으로 DataDelete메소드를 생성합니다.
@RequestMapping(value = "deleteData")
public @ResponseBody int deleteData(@RequestParam String code) {
int num = realGridDemoService.deleteData(code);
return num;
}
home.jsp로 돌아와서 삭제기능을 사용하기위해 setOptions를 다음과 같이 수정합니다.
function setOptions(grid) {
grid.setOptions({
edit: {
insertable: true,
appendable: true,
deletable: true,
confirmWhenDelete: true,
deleteRowsMessage: "Are you sure?",
},
});
dataProvider.setOptions({
softDeleting: true,
});
}
// btnSaveData도 수정해줍니다.
function btnSaveData() {
gridView.commit();
var currRow = gridView.getCurrent().dataRow;
if (currRow < 0) return;
var currState = dataProvider.getRowState(currRow);
if (currState == "created") {
saveData("addData");
} else if (currState == "updated") {
saveData("updateData");
} else if (currState == "deleted") {
saveData("deleteData");
}
}
위의 코드와 같이 현재 그리드 상태가 DELETED인지 확인하고 deleteData를 호출하도록 수정하게 됩니다.
마찬가지로 mapper안의 sample.xml, DAO, Service에도 추가해줍니다.
<delete id="deleteData" parameterType="String" >
DELETE sample
WHERE code = #{code}
</delete>
RealGridDemoDAO.java
public interface RealGridDemoDAO {
public List<RealGridDemoVO> getDatas();
public int addData(RealGridDemoVO realGridDemoVO);
public int updateData(RealGridDemoVO realGridDemoVO);
public int deleteData(String code);
}
RealGridDemoService.java
@Service
public class RealGridDemoService {
@Autowired
RealGridDemoDAO realGridDemoDAO;
public List<RealGridDemoVO> getDatas() {
return realGridDemoDAO.getDatas();
}
public int addData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.addData(realGridDemoVO);
}
public int updateData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.updateData(realGridDemoVO);
}
public int deleteData(String code) {
return realGridDemoDAO.deleteData(code);
}
}
자료를 조회 후 CTRL+DEL 키를 입력하여 삭제 상태로 바꾼 후 statusBar 영역이 어떻게 바뀌었는지 확인해보세요.
저장버튼을 눌러 삭제를 했음에도 불구하고 DB에서는 삭제가 되었지만 그리드에서는 데이터가 남아 있음을 확인할 수 있습니다. 이 경우 dataProvider.clearRowStates(true)로 설정하여 해결하실 수 있습니다.
function saveData(urlStr) {
var jRowData = dataProvider.getJsonRow(gridView.getCurrent().dataRow);
$.post(urlStr, jRowData, function (data) {
if (data > 0) {
alert("저장 성공");
dataProvider.clearRowStates(true);
} else {
alert("저장 실패!");
}
});
}
Save All Data
지금까지의 작업은 모두 행 단위 작업이었습니다. 업무 처리를 하다보면 행 단위 작업이 아닌 일괄 작업이 필요한 경우가 생길 수 있습니다.
지금부터는 RealGrid를 사용하여 INSERT, UPDATE, DELETE를 일괄로 처리하는 방법으로 해보도록 하겠습니다.
home.jsp에 btnSaveAllData버튼을 추가하고 해당 메소드를 작성합니다.
<button onClick="btnSaveAllData()">btnSaveAllData</button>
function btnSaveAllData(e) {
gridView.commit();
savedataAll("saveAllData");
}
function savedataAll() {
var state;
var jData;
var jRowsData = [];
var rows = dataProvider.getAllStateRows();
if (rows.updated.length > 0) {
$.each(rows.updated, function (k, v) {
jData = dataProvider.getJsonRow(v);
jData.state = "updated";
jRowsData.push(jData);
});
}
if (rows.deleted.length > 0) {
$.each(rows.deleted, function (k, v) {
jData = dataProvider.getJsonRow(v);
jData.state = "deleted";
jRowsData.push(jData);
});
}
if (rows.created.length > 0) {
$.each(rows.created, function (k, v) {
jData = dataProvider.getJsonRow(v);
jData.state = "created";
jRowsData.push(jData);
});
}
if (jRowsData.length == 0) {
dataProvider.clearRowStates(true);
return;
}
}
JSON Array로 넘겨받은 데이터를 루프 돌면서 RowState를 확인하고, 해당 액션에 맞게 처리합니다. dataProvider.getAllStateRows() 함수를 이용하면 state가 none이 아닌 row번호를
{
updated: Array[0],
deleted: Array[0],
created: Array[0],
createAndDeleted: Array[0]
}
형식으로 가져올수 있습니다.
다건의 데이터를 받으려면 List형식의 빈으로 받아야 하는데 Spring 3.9에서는 @ModelAttribute 나 @RequestBody로 List로 받는건 안됩니다. 스프링을 4.0으로 업그레이드를 하거나 아니면 JSON.stringify함수를 이용해서 String 타입의 JsonArray로 서버로 Request 합니다.
본예제는 Spring 3.9에서 구현 하였습니다.
먼저 Product 빈에 새로 추가된 state를 받을 변수를 추가합니다.
public class RealGridDemoVO {
private String code;
private String product_name;
private String volume;
private String unit;
private int price;
private String state;
public String getCode() {
return code;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public void setCode(String code) {
this.code = code;
}
public String getProduct_name() {
return product_name;
}
public void setProduct_name(String product_name) {
this.product_name = product_name;
}
public String getVolume() {
return volume;
}
public void setVolume(String volume) {
this.volume = volume;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Controller, Service, DAO를 작성합니다.
Controller.java
@RequestMapping(value = "saveAllData", method = RequestMethod.POST)
public @ResponseBody int saveAllData(@RequestBody String sampleStringList) {
List<RealGridDemoVO> sampleList = new ArrayList<RealGridDemoVO>();
JSONArray sampleJson = JSONArray.fromObject(JSONSerializer.toJSON(sampleStringList));
for (int i = 0; i < sampleJson.size(); i++) {
RealGridDemoVO sample = (RealGridDemoVO) JSONObject.toBean(sampleJson.getJSONObject(i), RealGridDemoVO.class);
sampleList.add(sample);
}
return realGridDemoService.saveAllData(sampleList);
}
RealGridDemoService.java
@Service
public class RealGridDemoService {
@Autowired
RealGridDemoDAO realGridDemoDAO;
public List<RealGridDemoVO> getDatas() {
return realGridDemoDAO.getDatas();
}
public int addData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.addData(realGridDemoVO);
}
public int updateData(RealGridDemoVO realGridDemoVO) {
return realGridDemoDAO.updateData(realGridDemoVO);
}
public int deleteData(String code) {
return realGridDemoDAO.deleteData(code);
}
@Transactional
public int saveAllData(List<RealGridDemoVO> realGridDemoVO) {
int num = 0;
for(RealGridDemoVO sampleVO1 :realGridDemoVO){
if(sampleVO1.getState().equals("created"))
num += realGridDemoDAO.addData(sampleVO1);
else if(sampleVO1.getState().equals("updated"))
num += realGridDemoDAO.updateData(sampleVO1);
else if(sampleVO1.getState().equals("deleted"))
num += realGridDemoDAO.deleteData(sampleVO1.getCode());
}
return num;
}
}
@Transactional 어노테이션을 붙여서 데이터 저장 오류시 Rollback할 수 있도록 작성합니다.
RealGridDemoDAO.java
public interface RealGridDemoDAO {
public List<RealGridDemoVO> getDatas();
public int addData(RealGridDemoVO realGridDemoVO);
public int updateData(RealGridDemoVO realGridDemoVO);
public int deleteData(String code);
public int saveAllData(List<RealGridDemoVO> realGridDemoVO);
}
이후 테스트를 하면 성공적으로 잘 변경되는것을 확인할 수 있습니다.
Commit By Cell
추가적으로 몇가지 기능들을 살펴보겠습니다. 그리드의 데이터를 수정 후, 지금까지는 그리드의 포커스 위치가 다른 행으로 이동해야만 해당 row의 상태값이 UPDATED로 변경되었습니다. 셀 단위로 commit을 지정하여, 수정하고 다른 행으로 이동하지않고 수정 후 ENTER키를 누르거나 다른 컬럼으로 이동시에 commit을 할 수 있도록 해주는 옵션을 설정 해보겠습니다.
function setOptions(grid) {
grid.setOptions({
edit: {
commitByCell: true,
// 셀 단위 수정 후 commit 여부
insertable: true,
appendable: true,
deletable: true,
deleteRowsConfirm: true,
deleteRowsMessage: "Are you sure?",
},
});
dataProvider.setOptions({
softDeleting: true,
});
}
commitByCell = false
수정 후 편집기가 종료되었을때의 모습입니다. 상태바의 상태가 편집상태인것을 확인할 수 있습니다.
commitByCell = true
수정 후 편집기가 종료되었을때, 상태바의 상태가 UPDATED로 변경된 것을 확인할 수 있습니다.
GridView와 DataProvider
지금까지는 gridView.beginInsertRow() 와 gridView.beginAppend() 를 사용해서 행을 추가했습니다. 행을 추가하기 위해서 위 2가지 방법 외에도 여러가지 방법이 있습니다.
행 추가 하기전 dataProvider 와 gridView의 차이점을 알고 있어야 합니다. 리얼그리드는 효과적인 데이터 관리를 위해 dataProvider가 별도의 데이터셋을 가지고 있고 gridView는 화면에 보여지는 데이터를 가지고 있습니다.
예를들면 그리드에서 정렬 기능을 사용하면 그리드에 표시되는 데이터의 순서가 변경됩니다. 그때 dataProvider의 데이터 순서는 병경하지 않고, gridView의 데이터 순서는 변경이 됩니다.
자세한 사항은 아래 링크를 참조하세요.
http://help.realgrid.com/tutorial/a10/ (opens in a new tab)
http://help.realgrid.com/tutorial/a11/ (opens in a new tab)
DataProvider의 insertRow, addRow
home.jsp에 버튼을 2개 더 추가해서 만들어보겠습니다.
<button onClick="btnProviderInsert()">프로바이더의 insertRow</button>
<button onClick="btnProviderAdd()">프로바이더의 addRow</button>
그리고 클릭이벤트 또한 추가해봅시다.
function btnProviderInsert() {
var tot = dataProvider.getRowCount();
var lastValue = dataProvider.getValue(tot - 1, 0);
var newValue = Number(lastValue.substr(-3)) + 1 + "";
dataProvider.insertRow(tot, {
code: "RG" + newValue.padStart(3, "0"),
});
}
function btnProviderAdd() {
var tot = dataProvider.getRowCount();
var lastValue = dataProvider.getValue(tot - 1, 0);
var newValue = Number(lastValue.substr(-3)) + 1 + "";
dataProvider.addRow({
code: "RG" + newValue.padStart(3, "0"),
});
}
dataProvider의 insertRow, addRow는 gridView의 beginInsertRow, beginAppendRow와는 다르게 매개변수로 기본값을 설정할 수 있습니다.
추가로, dataProvider의 행 추가는 실행이 되면 gridView의 행추가와는 다르게 상태바의 상태값이 편집상태가 아닌 CREATED로 변경된 것을 확인할 수 있습니다.
gridView의 행 추가
dataProvider의 행 추가
마지막으로 DB에서 Primary key로 설정되어있는 컬림인 코드컬럼의 수정을 불가능하게 하는것을 기본으로하고, 새로운 행이 추가되엇을때만 수정가능하도록 변경할 수 있게 설정해보도록 하겠습니다.
기본값으로 코드컬럼에 수정불가 옵션을 추가해보도록 하겠습니다.
{
fieldName : "code",
width : 180,
header : {
text : "코드"
},
editable : false,
},
동적으로 CREATED인 상태만 수정이 가능하도록 해야하기때문에 styleCallback 콜백함수를 작성합니다.
{
fieldName : "code",
width : 180,
header : {
text : "코드"
},
editable : false,
styleCallback: function(grid, model){
if(model.item.rowState == "created"){
return {
editable: true
}
}
}
},
위 코드를 작성하면 새로운행으로 추가된, 즉 상태가 CREATED인 행의 코드컬럼만 수정이 가능하고, 기존의 코드컬럼은 수정이 불가능한 것을 확인할 수 있습니다.
소스코드 다운로드