앞 게시글 Axboot 멀티 테이블 수정/저장 예제 (1/2) 에서 연속으로 진행하도록 하겠습니다.

이번 시간에는 4)제품 History 조회와 제품 정보의 판매가 수정 시 제품 History 테이블에 Insert하는 JAVA로직을 

처리하도록 하겠습니다.


4) 제품 History 조회와 제품 정보의 판매가 수정 시 제품 History 테이블에 Insert

   - PrdtPriceHist.java 수정


package com.dasdes.shopmng.domain.prdt;


import com.chequer.axboot.core.annotations.ColumnPosition;


import lombok.*;

import org.apache.ibatis.type.Alias;

import org.hibernate.annotations.CreationTimestamp;

import org.hibernate.annotations.DynamicInsert;

import org.hibernate.annotations.DynamicUpdate;

import org.hibernate.annotations.Type;


import com.chequer.axboot.core.annotations.Comment;

import com.dasdes.shopmng.domain.BaseJpaModel;

import com.dasdes.shopmng.domain.program.Program;


import java.sql.Timestamp;

import java.util.Date;


import javax.persistence.*;

import javax.validation.constraints.NotNull;


@Setter

@Getter

@DynamicInsert

@DynamicUpdate

@Entity

@EqualsAndHashCode(callSuper = true)

@IdClass(PrdtPriceHist.class)

@Table(name = "prdt_price_hist")

@Comment(value = "제품가격History")

@Alias("prdtPriceHist")

public class PrdtPriceHist extends BaseJpaModel<String> {


@Id

@Column(name = "id", length = 20, nullable = false)

@Comment(value = "id")

private String id;

@Column(name = "prdt_cd", length = 20, nullable = false)

@Comment(value = "제품코드")

private String prdtCd;


@Column(name = "ch_date", nullable = false)

@Comment(value = "변경일시")

@Temporal(TemporalType.TIMESTAMP)

private Date chDate;


@Column(name = "sales_price", precision = 10, nullable = false)

@Comment(value = "판매가격")

private Integer salesPrice;


@Override

public String getId() {

return id;

}



  - PrdtPriceHistRepository.java


package com.dasdes.shopmng.domain.prdt;


import com.chequer.axboot.core.domain.base.AXBootJPAQueryDSLRepository;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;


@Repository

public interface PrdtPriceHistRepository extends AXBootJPAQueryDSLRepository<PrdtPriceHist, String> {



  - PrdtPriceHistService.java


package com.dasdes.shopmng.domain.prdt;


import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import com.dasdes.shopmng.domain.BaseService;

import com.dasdes.shopmng.exception.DBException;

import com.querydsl.core.BooleanBuilder;


import javax.inject.Inject;

import com.chequer.axboot.core.parameter.RequestParams;


import java.util.ArrayList;

import java.util.List;


@Service

public class PrdtPriceHistService extends BaseService<PrdtPriceHist, String> {

    private PrdtPriceHistRepository prdtPriceHistRepository;


    @Inject

    public PrdtPriceHistService(PrdtPriceHistRepository prdtPriceHistRepository) {

        super(prdtPriceHistRepository);

        this.prdtPriceHistRepository = prdtPriceHistRepository;

    }


    //제품 판매가 History조회

    public List<PrdtPriceHist> gets(RequestParams<PrdtPriceHist> requestParams) {

    String prdtCd=requestParams.getString("prdtCd", "");

    String filter = requestParams.getString("filter");


    BooleanBuilder builder = new BooleanBuilder();


        if (isNotEmpty(prdtCd)) {

            builder.and(qProduct.prdtCd.eq(prdtCd));

        }


        List<PrdtPriceHist> prdtpriceHistList =  

                       select().from(qPrdtPriceHist).where(builder).orderBy(qPrdtPriceHist.prdtCd.asc()).fetch();


        if (isNotEmpty(filter)) {

        prdtpriceHistList = filter(prdtpriceHistList, filter);

        }   


        return prdtpriceHistList;

    }

    

    //제품 정보에서 호출하는 판매가 insert method

    @Transactional

    public void savePrdPrice(List<Product> product) {

    PrdtPriceHist prdtHist=null;

    List<PrdtPriceHist> hlist=new ArrayList();

    Product prdt=null;

   

    for(int i=0;i<product.size();i++)

    {

    prdt=(Product)product.get(i);

   

    if(prdt.is__modified__())

    {

    prdtHist=new PrdtPriceHist();

    prdtHist.setPrdtCd(prdt.getPrdtCd());

    prdtHist.setSalesPrice(prdt.getSalesPrice());

    prdtHist.setCreatedBy(prdt.getCreatedBy());

    hlist.add(prdtHist);

    }

    }        

   

    save(hlist);

    }

}

 


  - ProductService.java : 제품 정보에서 판매가를 수정할 때 제품 History 테이블에 insert되는 method 호출


package com.dasdes.shopmng.domain.prdt;


import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional; //추가


import com.querydsl.core.BooleanBuilder;  //추가


import javax.inject.Inject;

import com.chequer.axboot.core.parameter.RequestParams;

import com.dasdes.shopmng.domain.BaseService;

import com.dasdes.shopmng.exception.DBException;


import java.util.List;


//import org.slf4j.Logger;

//import org.slf4j.LoggerFactory;


@Service

public class ProductService extends BaseService<Product, String> {

    private ProductRepository productRepository;

    

    @Inject

    private PrdtPriceHistService prdtPriceHistService;


    @Inject

    public ProductService(ProductRepository productRepository) {

        super(productRepository);

        this.productRepository = productRepository;

    }


    public List<Product> gets(RequestParams<Product> requestParams) {

    String prdtCd=requestParams.getString("prdtCd", "");

    String prdtNm=requestParams.getString("prdtNm", "");

    String filter = requestParams.getString("filter");

   

    BooleanBuilder builder = new BooleanBuilder();



        if (isNotEmpty(prdtCd)) {

            builder.and(qProduct.prdtCd.eq(prdtCd));

        }



        if (isNotEmpty(prdtNm)) {

            builder.and(qProduct.prdtNm.eq(prdtNm));

        }


        List<Product> prdtList 

              = select().from(qProduct).where(builder).orderBy(qProduct.prdtCd.asc(), qProduct.prdtNm.asc()).fetch();


        if (isNotEmpty(filter)) {

        prdtList = filter(prdtList, filter);

        }

   

        return prdtList;

    }

    

    @Transactional

    public void savePrdt(List<Product> product) {

    Product prdt=null;

    for(int i=0;i<product.size();i++)

    {

    prdt=(Product)product.get(i);

    //logger.error(i+": {} :"+prdt.toString());

   

    if(prdt.is__created__() && exists(prdt.getId()))

    {

    throw new DBException("신규로 추가하신 "+prdt.getId()+"코드는 이미 등록된 제품코드입니다.");

    }

    }   

        save(product);


        //제품 판매가 History Table에 Insert되는 method호출

        prdtPriceHistService.savePrdPrice(product);

    }

}

 


5) 테스트

  - 서버 구동

  


   - 제품 정보에서 판매가 수정을 해본다. 


   - 제품 판매가 History 조회 : 하다보니 누가 변경했는지가 필요할 것 같은데 빠졌네요.^^

                                        그 부분은 여러분들이 한번 추가해 보세요..==>과제.


    

어때요? 도움이 많이 되셨나요? ~~~~~~~도움이 되셨다면 아래 꾹~~~



  1. graykang 2019.07.02 14:53

    안녕하세요.
    좋은 정보 너무 감사드리며, 글 잘 읽고 있습니다.
    혹시 사용자 관리쪽에 계정 추가 하고 해당 계정으로 로그인시 404에러가 뜨는데 사용자용 main 대시보드를 따로 연결해 주는 작업을 해야 하는건가요? 새로 추가한 페이지 들만 보여지는 일반 계정을 등록 해서 사용 해 볼려고 하는데 막혀서 혹시나 도움 받을수 있을까 해서 글 남깁니다.

    • graykang 2019.07.03 12:00

      음...해결 했어요 ㅎㅎ
      인터넷 임시 파일을 삭제 하니까 되네요 ^^;;

  2. dasdes 2019.07.03 12:05 신고

    다행이네요. 제가 컴터에 환경을 날려서 답변을 못드린점 양해부탁드립니다

[제품 가격 History]

두개 이상의 테이블을 컨트롤하는 예제를 처리하기 위해 기존 예제에 적합한

제품 가격이 변경될 때마다 그 내역을 관리할 수 있는 화면을 만들어 보기로 한다.



1. 작성순서

   1) 먼저 제품 가격 History를 저장할 수 있는 테이블을 생성하고 조회할 수 있는 화면을 만든다.

      (이 부분은 화면생성부분을 참고하기 바랍니다. 여기서는 대략적으로 설명할 예정입니다.)

      -Axboot 신규화면 만들기 1~3 참고       

       Axboot 신규화면 만들기 1 - DB생성 및 서버단 소스생성

       Axboot 신규화면 만들기 2 - 화면 소스 만들기

       Axboot 신규화면 만들기 3 - 정상적인 CRUD를 위한 JAVA소스 수정

   

   2) 이전 단계에서 만든 제품정보의 판매가격 변경 시 제품 가격 History테이블에 저장하는 로직을 추가한다.


2. 구현

   1) 제품 History테이블 생성      

CREATE TABLE `prdt_price_hist` (
	`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
	`prdt_cd` VARCHAR(20) NOT NULL COMMENT '제품코드',
	`ch_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '변경일시',
	`sales_price` INT(10) NOT NULL DEFAULT '0' COMMENT '판매가격',
	`CREATED_AT` DATETIME(6) NULL DEFAULT NULL,
	`CREATED_BY` VARCHAR(255) NULL DEFAULT NULL,
	`UPDATED_AT` DATETIME(6) NULL DEFAULT NULL,
	`UPDATED_BY` VARCHAR(255) NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)
COMMENT='제품가격History'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=4

  2) JAVA 코드 생성

      : http://localhost:8080/modelExtractor/db 접속 후 위에서 만들어진 테이블 클릭 후 기본 JAVA코드를 생성하자.
        (위 Axboot 신규화면 만들기 1 참조 - 코드생성 시 Package Name : prdt , Class Name : PrdtPriceHist 로 입력)    

  3) 제품 가격 History 화면소스 만들기

      : 간단히 기존에 만들었던 제품 정보 화면 소스를 복사해서 PrdtPriceHist.jsp와 PrdtPriceHist.js 로 

        이름을 변경한 후 일부만 수정하면 된다.

        (추가적으로 위 링크의 화면소스만들기 2를 참고하여 webapp하위의 axboot.config.js 에 js에서 호출할 

         약식이름과 full명의 mapping설정한다. - "prdtPriceHist":"/api/v1/prdtPriceHist")

        

     - PrdtPriceHist.jsp 수정     

<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="ax" tagdir="/WEB-INF/tags" %>


<ax:set key="system-common-code-version" value="1.0.0"/>

<ax:set key="title" value="${pageName}"/>

<ax:set key="page_desc" value="${pageRemark}"/>

<ax:set key="page_auto_height" value="true"/>


<ax:layout name="base">

    <jsp:attribute name="script">

        <ax:script-lang key="ax.script" var="LANG" />

        <ax:script-lang key="ax.admin" var="COL" />

        <script type="text/javascript" src="<c:url value='/assets/js/view/shopmng/prdtPriceHist.js' />"></script>

    </jsp:attribute>

    <jsp:body>


        <ax:page-buttons></ax:page-buttons>


        <div role="page-header">

            <ax:form name="searchView0">

                <ax:tbl clazz="ax-search-tbl" minWidth="500px">

                    <ax:tr>

                        <ax:td label='ax.admin.search' width="300px">

                            <ax:input type="text" name="filter" id="filter" clazz="form-control" placeholder="ax.admin.input.search"/>

                        </ax:td>

                    </ax:tr>

                </ax:tbl>

            </ax:form>

            <div class="H10"></div> 

        </div>


        <ax:split-layout name="ax1" orientation="horizontal">

            <ax:split-panel width="*" style="">


                <!-- 목록 -->

                <div class="ax-button-group" data-fit-height-aside="grid-view-01">

                    <div class="left">

                        <h2>

                            <i class="cqc-list"></i>

                            <!--<ax:lang id="ax.admin.commoncode.title"/>-->

                             제품 가격 변경 내역

                        </h2>

                    </div>

                    <div class="right">

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="add"><i class="cqc-circle-with-plus"></i> <ax:lang id="ax.admin.add"/></button>

                        <button type="button" class="btn btn-default" data-grid-view-01-btn="delete"><i class="cqc-circle-with-minus"></i> <ax:lang id="ax.admin.delete"/></button>

                    </div>

                </div>

                <div data-ax5grid="grid-view-01" data-fit-height-content="grid-view-01" style="height: 300px;"></div>


            </ax:split-panel>

        </ax:split-layout>



    </jsp:body>

</ax:layout>


PrdtPriceHist.js 수정

var fnObj = {};

var ACTIONS = axboot.actionExtend(fnObj, {

    PAGE_SEARCH: function (caller, act, data) {

        axboot.ajax({

            type: "GET",

            url: ["prdtPriceHist"],

            data: caller.searchView.getData(),

            callback: function (res) {

                caller.gridView01.setData(res);

            }

        });

        return false;

    },

    PAGE_SAVE: function (caller, act, data) {

        var saveList = [].concat(caller.gridView01.getData("modified"));

        saveList = saveList.concat(caller.gridView01.getData("deleted"));


        axboot.ajax({

            type: "PUT",

            url: ["prdtPriceHist"],

            data: JSON.stringify(saveList),

            callback: function (res) {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

                axToast.push(LANG("onsave"));

            }

        });

    },

    ITEM_ADD: function (caller, act, data) {

        caller.gridView01.addRow();

    },

    ITEM_DEL: function (caller, act, data) {

        caller.gridView01.delRow("selected");

    }

});


//기존 제품정보 복사 시 이 부분을 초기 셋팅으로만 변경해준다.

fnObj.pageStart = function () {

    this.pageButtonView.initView();

    this.searchView.initView();

    this.gridView01.initView();


    ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

};


fnObj.pageResize = function () {


};


fnObj.pageButtonView = axboot.viewExtend({

    initView: function () {

        axboot.buttonClick(this, "data-page-btn", {

            "search": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);

            },

            "save": function () {

                ACTIONS.dispatch(ACTIONS.PAGE_SAVE);

            }

        });

    }

});


//== view 시작

/**

 * searchView

 */

fnObj.searchView = axboot.viewExtend(axboot.searchView, {

    initView: function () {

        this.target = $(document["searchView0"]);

        this.target.attr("onsubmit", "return ACTIONS.dispatch(ACTIONS.PAGE_SEARCH);");

        this.filter = $("#filter");

    },

    getData: function () {

        return {

            pageNumber: this.pageNumber,

            pageSize: this.pageSize,

            filter: this.filter.val()

        }

    }

});



/**

 * gridView

 */

fnObj.gridView01 = axboot.viewExtend(axboot.gridView, {

    initView: function () {

        var _this = this;

         

        this.target = axboot.gridBuilder({

            showRowSelector: true,

            frozenColumnIndex: 0,

            sortable: true,

            multipleSelect: true,

            target: $('[data-ax5grid="grid-view-01"]'), 

            columns: [

            {key: "id", label: "id", display: false},

            {key: "prdtCd", label: "제품코드", width: 100, align: "center", editor: "text"},  

                //내부적으로 update 일자를 관리하고 있지만, 그냥 넣어봤음.

                {key: "chDate", label: "변경일시", width: 150, align: "right", editor: "date"},  

                {key: "salesPrice", label: "판매가격", width: 150, align: "right", editor: "number"}          

            ],

            body: {

                onClick: function () {

                    this.self.select(this.dindex, {selectedClear: true});

                }

            }

        });


        axboot.buttonClick(this, "data-grid-view-01-btn", {

            "add": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_ADD);

            },

            "delete": function () {

                ACTIONS.dispatch(ACTIONS.ITEM_DEL);

            }

        });

    },

    getData: function (_type) {

        var list = [];

        var _list = this.target.getList(_type);


        if (_type == "modified" || _type == "deleted") {

            list = ax5.util.filter(_list, function () {

                return this.prdtCd;

            });

        } else {

            list = _list;

        }

        return list;

    },

    addRow: function () {

        this.target.addRow({__created__: true}, "last");

    }

}); 


그 다음은 서버를 실행하여 프로그램 및 매뉴관리 화면에서 제품가격History를 추가해준다.


   


나머지 조회 및 Update(제품정보의 판매가격 수정시 호출)을 위한 JAVA소스 수정은 다음에 게시하고,

오늘은 여기까지만 하도록 하겠습니다. 



    

      

1. 설치목적

  - 이 블로그에서 postgresql DB를 설치하는 목적은 시각화 분석시스템인 superset (https://dodo-it.tistory.com/71)에서

   postgresql을 연결해 보기 위함이다.


2. 설치순서

   1) CentOS7에서는 yum으로 쉽게 설치된다. 

 # sudo yum install postgresql-server postgresql-contrib


   2) 초기 DB구성 설치

 # sudo postgresql-setup initdb


   3) 암호인증 허용하기 위한 설정 편집      

      - vi 에디터로 설정파일을 OPEN한다.

 # vi /var/lib/pgsql/data/pg_hba.conf

      - 열려진 에디터 창에서 Ctrl + G 를 눌러 한번에 제일 아래로 이동한다.(제일 아래쪽에 수정내용이 있다)

        그러면 아래 부분이 보인다. (수정할 부분은 빨간색 칠한부분이다)B


# "local" is for Unix domain socket connections only

local   all             all                                     md5

# IPv4 local connections:

host    all             all             127.0.0.1/32            ident

# IPv6 local connections:

host    all             all             ::1/128                 ident

# Allow replication connections from localhost, by a user with the

# replication privilege.

#local   replication     postgres                                peer

#host    replication     postgres        127.0.0.1/32            ident

#host    replication     postgres        ::1/128                 ident



     - 해당 위치로 커서 이동 후 i 를 눌러 편집모드에서 ident를 지우고 그 위치에 md5를 입력한다.

       또한 본인의 경우 virtualbox를 사용하여 virtualbox 아이피도 추가해준다.

       최종결과는 아래와 같은 모습이다.


# "local" is for Unix domain socket connections only

local   all             all                                     md5

# IPv4 local connections:

host    all             all             127.0.0.1/32            md5

# IPv6 local connections:

host    all             all             ::1/128                 md5

host    all             all             10.0.2.15/24            md5

# Allow replication connections from localhost, by a user with the

# replication privilege.

#local   replication     postgres                                peer

#host    replication     postgres        127.0.0.1/32            ident

#host    replication     postgres        ::1/128                 ident



   - vi 편집을 종료,저장하고 나가기 위해 esc 한번 ":"(콜론) 입력 wq 입력 enter 하면 완료된다.

 :wq


   - DB를 구동해본다.

 # sudo systemctl start postgresql

   

   - 시작시 자동으로 시작되도록 설정 

 # sudo systemctl enable postgresql

   

   - 외부접속가능하도록 방화벽을 열어준다.     

# firewall-cmd --permanent --zone=public --add-port=5432/tcp 

# firewall-cmd --reload


3. 기타 사용법

   1) 초기접속은 postgres 계정으로접속한다. (sudo -i -u postgres  --> password입력 --> psql 입력)

      * 초기 postgres 비번변경필요시 # sudo passwd postgres 명령어 사용


# sudo -i -u postgres

-bash-4.2$ psql

Password:

psql (9.2.24)

Type "help" for help.


postgres=#



  2) postgres=# 프롬프트에서 빠져나가기  

 postgres=# \q

      이후 아래 프롬프트 나오면 exit 입력후 엔터      

 -bash-4.2exit


  3) DB 생성하기(superset db를 생성하기로 함) 

 postgres=# CREATE DATABASE superset OWNER postgres;


  4) 생성된 DB List 보기 (역슬레쉬 + 소문자 엘)

 postgres=# \l

      * 결과        List of databases


   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges

-----------+----------+----------+-------------+-------------+-----------------------

 postgres  | postgres | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 |

 sharkdb   | sharkdb  | UTF8     | en_US.UTF-8 | en_US.UTF-8 | sharkdb=CTc/sharkdb  +

           |          |          |             |             | =CTc/sharkdb

 superset  | postgres | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 | =Tc/postgres         +

           |          |          |             |             | postgres=CTc/postgres+

           |          |          |             |             | soo=CTc/postgres

 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +

           |          |          |             |             | postgres=CTc/postgres

 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +

           |          |          |             |             | postgres=CTc/postgres

(5 rows)




어때요? 도움이 많이 되셨나요? ~~~~~~~도움이 되셨다면 아래 꾹~~~


+ Recent posts