달려라코난
72
2019-10-18 16:12:43
20
363

스프링에서 mybatis시 디릭토리 경로설정문제


안녕하세요.

mybatis에서 postgresql 연동하였고, mapper.xml에서 


<insert id = "insertTable">

  COPY ${tablename} from #{filepath} with csv delimiter ',' 

</insert>

문법으로 테이블에 데이터를 넣고 싶은데요. 테이블에 데이터를 넣지 못하고 에러가나네요.

에러메세지는....

### SQL : COPY tablename FROM ? with csv delimiter ',' 입니다.


filepath안에는 확실히 파일이 존재해요. 어떻게해야 에러가 안날까요??


0
0
  • 답변 20

  • 제타건담
    6k
    2019-10-18 16:31:32
    0
  • 달려라코난
    72
    2019-10-21 11:05:23

    답변감사합니다. 

    그런데 제가 완전 초급이어서 조금 더 상세하게 알려주실수 있으세요?

    0
  • 제타건담
    6k
    2019-10-21 13:25:00

    그러시면..기존 코드에서 java가 mybatis 연동해서 사용하는 코드를 보여주세요..

    만약 spring이시면 @Repository 어노테이션 붙은 코드 아무거나 하나랑..spring bean 설정에서 data source 설정한 부분을 보여주세요..

    spring을 사용중이 아니시면 db connection을 얻어와서 sql 문을 실행하는 부분을 보여주세요

    0
  • 달려라코난
    72
    2019-10-21 14:12:20

    1.root-context.xml 파일 내용

    <bean="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value=${jbdc.driverclass} />
    <property name="url" value=${jbdc.url} />
    <property name="username" value=${jbdc.username} />
    <property name="password" value=${jbdc.password} />
    </bean>

    jdbc.properties 파일 내용

    jdbc.driverClassName=org.postgressql.Driver
    jdbc.url=jdbc:postgresql://xxx.xxx.xxx.xxx
    jdbc.username=xxxxx
    jdbc.password=xxxxx

    2. @Repository("uploadDAO")
    public class UploadDAO {

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    public int addInsetTable(String tableName, String[] headdata, String tableData) {

    Map<Stirng, object> parameters = new HashMap<String, object();
    parameters.put("tableName", tableName);
    parameters.put("headData", headData);
    parameters.put("tableData, tableData);

    return sqlSessionTemplate.insert("uploadDAO.addInsertTable",parameters);
    }

    }


    입니다. 제가 페쇄망이라 일일이 모바일로 타이핑 한거라 중간에 오타가 있을수는있는데 이런흐름입니다.

    0
  • 제타건담
    6k
    2019-10-21 16:12:17 작성 2019-10-21 19:53:05 수정됨

    일단은 저도 직접 제가 코딩을 하며 검증을 한것은 아니기 때문에 아래의 코드는 일종의 가이드라인..이라고 생각하시면 됩니다..그러나 Connection 객체 받아오는 것은 지금 하시고 계신 환경에서는 아래 코드와 같이 하시면 되요..
    설명은 코드에 주석을 달았으니 그걸 보시면 될듯하고..그래도 이해가 안되는 부분이 있음 댓글로 남겨주세요..바로 답을 해드릴수는 없겠지만..보는대로 답은 올려드릴께요..

    @Repository("uploadDAO")
    public class UploadDAO {
    
        @Autowired
        private SqlSessionTemplate sqlSessionTemplate;
    
        /*
        * 이 메소드는 아래의 copyTable 메소드를 어떻게 사용하는지를 보여주는 sample 코드임
        * 이 메소드 코드는 @Service 어노테이션이 붙은 클래스에서 이러한 형태로 사용하면 됨
        * @Repository 어노테이션이 붙은 클래스에 copyTableFromFileUseServiceClass 메소드를 넣지 말것
        * 구체적으로 일을 하는 메소드는 아래에 있는 copyTableFromFile 메소드로서 범용적으로 설계한거임
        * 파일 경로와 테이블명과 구분자를 파라미터로 받게 해서 어떤 파일이나 테이블이나 구분자에서도 사용가능하게끔 설계
        */
        public long copyTableFromFileUseServiceClass() throws Exception {
            copyTableFromFile("C:/test/mytest.csv", "MYTABLE", ",");
            
        }
    
        /**
        * 첫번째 파라미터는 처리하고자 하는 파일 경로와 파일 이름이 full로 들어간것(윈도우에 있는 파일이면 경로 구분자인 \을 /로 변환)
        * 두번째 파라미터는 작업하고자 하는 테이블명
        * 세번째 파라미터는 구분자(delimiter)
        * return 된 값은 반영된 레코드 수를 return 하는 것이기 때문에 0이 아니라면 실제 들어간 레코드 값이 존재한다는 뜻
        * 다만 이 코드는 transaction 까지 검증된 것은 아니기 때문에 문제가 발생할 경우 기존 등록된 레코드가 rollback이 되는지 확인은 필요
        * 만약 rollback이 안되면 try-catch 로 바꿔서 인위적으로 commit, rollback을 해야 함
        * 현재는 Spring에게  transaction을 위임하고 있는 상황임
        */
        public long copyTableFromFile(String filePath, String tableName, String delimiter) throws Exception {
            Connection connection = sqlSessionTemplate.getConnection();
            String sql = "COPY " + tableName + " from " + filePath + " with csv delimiter '" + delimiter + "' ";
            CopyManager copyManager = new CopyManager(connection);
            Reader in = new BufferedReader(new FileReader(new File(filePath)));
            long rowsaffected = copyManager.copyIn(sql, in);
            return rowsaffected;
        }
    
    }


    postgresql 에서 잘 실행되던 명령문이면 큰 문제는 없을듯 한데..저도 직접 해본건 아니어서..장담은 못드리겠네요..

    0
  • 달려라코난
    72
    2019-10-21 19:27:20
    상세한 답변감사드립니다.
    한가지 더 여쭤볼게 있는데요.

    DAO에 

    public long copyTableFromFileUseServiceClass() throws Exception { copyTableFromFile("C:/test/mytest.csv", "MYTABLE", ","); } 

    위 구문이 왜 있는지 모르겠어요..

    저는 UploadImpl.java 라는 서비스단에서
    uploadDAO.addInsertTable(tablename, headdata, tabledata); 
    로 구현했는데요.

    이부분을 알려주신 저 부분으로 수정하면 되나요??
    0
  • 달려라코난
    72
    2019-10-21 19:48:42

    mapper 에서


    <insert id = "insertTable">

      COPY ${tablename} from #{filepath} with csv delimiter ',' 

    </insert>

    위 구문은 지워야하나요??


    0
  • 제타건담
    6k
    2019-10-21 19:50:35 작성 2019-10-21 19:58:54 수정됨

    네..이거는 mapper를 사용하지 않고 하는거에요..mybatis 연동과는 무관하게 하는겁니다..


    지금보니 #{filepath} 로 적었네요..수정할께요..


    글고 copyTableFromFileUseServiceClass 이 메소드는 주석에도 설명했다시피 @Service 어노테이션이 붙은 클래스에서 copyTableFromFile 메소드를 어떤식으로 사용하는지를 보여주기 위해 만든거에요..
    그 부분을 설명해줄려면 @Service 어노테이션이 붙은 클래스까지 알아야 하기 때문에 메소드를 어떻게 사용하면 되는지를 @Repository 어노테이션이 붙은 클래스에서 해준것뿐이지 @Repository 어노테이션이 붙은 클래스에 copyTableFromFileUseServiceClass() 를 넣으라는게 아니에요..

    UploadImpl.java 에서 uploadDAO.copyTableFromFile("C:/test/mytest.csv", "MYTABLE", ",");
    요렇게 사용하시면 됩니다..

    0
  • 달려라코난
    72
    2019-10-22 08:44:50 작성 2019-10-22 09:01:47 수정됨

    상세한 답변감사합니다. 

    어떻게 해야하는지 알겠어요ㅎㅎ

    한가지 더 여쭤볼게 있는데요. 

    CopyManager copyManager = new CopyManager(connection) 에서 에러문구가 나는데요.


    import org.postgresql.core.BaseConnection;

    .

    .


    CopyManager copyManager = new CopyManager((BaseConnection)  connection);

    으로수정하라고하는데요 이부분 수정해도 상관없는건가요??

    Run 하면 org.postgresql.util.PSQLException: This connection has been closed. 에러가 나는데.. 어디가 눈제인지 혹시 이부분도 아실까요??

    0
  • 제타건담
    6k
    2019-10-22 12:38:26 작성 2019-10-22 12:46:32 수정됨

    아..지금보니 BaseConnection으로 변환을 해야 하네요..네..그렇게 수정해주세요..

    connection closed 가 나오는 이유는..봐야 할듯 한데..


    그러면 다음의 코드를 Connection connection = sqlSessionTemplate.getConnection(); 다음에 넣어서 Conneciton이 끊겼는지 확인해보세요..


    boolean isClosed = connection.isClosed();
    if(isClosed) {
       System.out.println("Connection is closed");
    }else{
       System.out.println("Connection is not closed");
    }


    Connection is closed 문자열이 출력되면 DB 연결이 끊어진거에요..근데 그런거면 SQL 작업 자체를 모두 할 수 없게 되는데..흠..


    그리고 지금 보니까 DataSource 클래스를 DriverManagerDataSource를 사용하고 있는데요..이것은 Connection Pool이 아니에요..외형적으로는 Connection Pool 형태를 가지고 있으나 내부적인 동작은 SQL 실행 요청이 있을때마다 매번 Connection을 생성하기 때문에 이 부분은 수정하셔야 합니다..


    http://blog.naver.com/PostView.nhn?blogId=admass&logNo=220979632501&beginTime=0&jumpingVid=&from=search&redirect=Log&widgetTypeCall=true


    요거 한번 읽어보시고 hikaricp로 교체하세요..

    0
  • 달려라코난
    72
    2019-10-23 11:05:13

    자세한 답변 정말 감사드립니다.

    거의 다 온것 같은데 connection이 안되네요.

    어제는 하루종일 구글링해보면서 해결해보려고 해도 성과가 없네요.

    system.out.println 했을때 connection is closed로 나오네요.

    mybatis 연동하고 mapper에 create table는 만들어지는데

    그 이후 copy작업시에는 connection이 안되네요.

    혹시 Connection connection = sqlSessionTemplate.getConnection 구분에서 사용하는 설정파일이 있는건가요?? 

    0
  • 제타건담
    6k
    2019-10-23 13:58:39

    혹시..기존에 sqlsessiontemplate 사용해서 정상적으로 동작하는 메소드에도 connection 얻어오는 부분과 connection 체크하는 부분을 메소드 처음 부분에 넣어서 확인해주실수 있나요..?

    저게 closed 로 나오면 좀 이상한데..

    만약 그래도 closed로 나오면 datasource 를 hikaricp 같은 진정한 connection pool 로 바꾸어서 테스트해보거나..

    아니면 예전 jdbc 프로그래밍 하듯이 connection을 인위적으로 별도로 맺어서 진행하고 해제하는 식으로 가야 할듯..


    0
  • 달려라코난
    72
    2019-10-23 14:33:44

    uploadDAO.java 라는 파일에서


    @Repository("upload")

    public clasd Upload {


    @Autowired

    private SqlSessionTemplTe sqlSessionTemplate;

    public List<obejct> addCreateTable(String tableName, String[] tableData) {

    map<String, object> parameters = new HashMap<String, object();

    parameters.put("tableName", tableName);

    parameters.put("tableData", tableData);


    return sqlSessionTemplate.selectList("uploadDAO.addCreteTable", parameters);

    }

    이부분 말씀하시는건가요??


    0
  • 제타건담
    6k
    2019-10-23 15:31:57

    addCreateTable 메소드가 정상적으로 동작하는 메소드인가요..?

    아니면 @Repository 어노테이션 달린 클래스가 Upload 클래스만 있는건 아니실꺼자너요..

    기존 다른 @Repository 어노테이션 달린 클래스에서 정상적으로 동작하는 메소드에다가 한번 Connection 받아서 close 되는 상황인지 체크를 해봐야 할 것 같아서요..

    0
  • 달려라코난
    72
    2019-10-23 18:29:23

    답변 감사드립니다.

    알려주신소스에서

    String url = jdbc//xxx.xxx.xxx.xxx

    String username = xxxxx

    String password = xxxxx

    Connection connection = DriverManager.getConnection(url,username,password)

    이하 문장은 같음...

    으로 처리했는데 되더라고요..

    첫번째 질문. Connection 설정이 문제가 있었던것 같은데 저런 설정들은 어느 파일에 정의를 해줘야하나요?


    두번째 질문. addCreateTable은 정상적으로 실행되어 DB에 테이블 생성이 되었습니다. Connection 설정이 문제였다면 테이블은 어떻게 생성이 되었을까요..


     public long copyTableFromFile(String filePath, String tableName, String delimiter) throws Exception {
            Connection connection = sqlSessionTemplate.getConnection();
            String sql = "COPY " + tableName + " from " + filePath + " with csv delimiter '" + delimiter + "' ";
            CopyManager copyManager = new CopyManager(connection);
            Reader in = new BufferedReader(new FileReader(new File(filePath)));
            long rowsaffected = copyManager.copyIn(sql, in);
            return rowsaffected;
        }0
    0
  • 제타건담
    6k
    2019-10-23 21:28:16

    이게..어디까지나..추측으로만 얘기하는거라..그걸 검증할려면 저로서는 좀더 소스를 봐야 할것 같은데..현재로서는 확인할수는 없네요..

    지금의 제 추측은 머냐면..DataSource를 만들때 문제가 있는거 같아요..DriverManagerDataSource 클래스의 동작 원리가 아마 추측엔 실제 쿼리문을 실행하는 시점에 그때 Connection을 맺는거 같아요..그래서 잘 돌아가는 다른 메소드가 있으면 거기에서도 closed로 나오는건지 확인해달라고 했던거구요..

    그리고 DriverManager 클래스를 이용해서 하는것은 예전 jdbc 스타일입니다..그러나 이것도 주의해야 할 것이 2개가 있어요..

    일단 첫번째는 Spring에서 제공하는 Connection이 아닌 본인이 직접 별도로 Connection을 맺은 것이기 때문에 connection을 close 하는 것도 본인이 직접 해줘야 합니다. Spring의 DataSource의 경우는 이러한 것을 Spring이 직접 하기 때문에 문제가 없지만 이걸 사용하면 본인이 Spring과는 무관하게 DB 접속 Connection을 따왔기 때문에 이것에 대해서 close도 본인이 직접해줘야 합니다. 그러지 않으면 나중에 Connection Full 에러가 발생해서 DB 접속 자체가 안되는경우도 있어요..

    그리고 두번째는 Transaction 관련 부분입니다. Spring의 경우 DataSource를 등록하고 TransactionManager를 등록하고 이 둘을 서로 연결지어놓으면 transaction을 직접 관리하게 되어서 DB 작업시 문제가 없으면 commit 하고 문제가 있으면 rollback 하지만 이렇게 본인이 직접 Connection을 만들어서 하게 되면 Transaction 또한 본인이 직접 제어해야 합니다. 

    Connection을 받을때 setAutoCommit(false)를 해줘서 자동으로 commit이 안되게 하고 작업에 문제가 없으면 connection.commit(); 문제가 있으면 connection.rollback(); 그리고 작업이 다 완료되면 connection.close(); 이렇게 실행하면서 이 모두를 try-catch 구문 안에 두어야 되요..Spring은 이것들을 모두 자동으로 해주지만 지금 DriverManager 클래스를 이용해서 본인이 직접 Connection을 맺는 방식으로 접근하면 이러한 관리를 본인이 직접 해줘야 합니다.

    이 부분은 제가 댓글로 설명해드리기엔 너무 길고..jdbc 프로그래밍에 대해 검색해서 공부해보시면 이해하실수 있을꺼에요..Spring은 jdbc 프로그래밍에서 사용자가 DB connecton을 맺고 Transaction 제어하는 부분을 개발자가 신경쓰지 않아도 되게끔 해준겁니다. 그러나 Connection을 본인이 직접 맺어버리면 이걸러한 부분을 본인이 직접 다 챙겨야 되요.. 


    0
  • 달려라코난
    72
    2019-10-24 11:09:18

    아!! 자세한 설명 감사드립니다.

    우선 connection에 대해서 더공부해서 DriverManager이 아닌 SqlsessionTemplate로 연결해봐야겠네요.

    그동안 답변 너무 감사드립니다.!! 덕분에 COPY 구문은 해결이 되어서 큰성과가 있었네요.

    저도 더 공부해서 여기에 답변을 달아보도록해야갰어요ㅎㅎ

    0
  • 달려라코난
    72
    2019-10-24 15:35:39

    혹시 메일주소 알려주실수 있으실까요??

    염치없지만 소스를 통으로 보내서 문제점이 뭔지 알구싶어서요..


    어렵네요 ㅠ

    0
  • 제타건담
    6k
    2019-10-24 19:41:09

    제게 소스를 통으로 보내셔도 제가 봐드릴순 없어요..
    소스만으로는 동일한 환경을 만들수는 없죠..연동하는 DB 라든가 부가적인 시스템들이 있기 때문에..

    그래서 어떤것이 안풀린다..하는 한건한건 이런 것들은 머..에러로그 같은거 보며 잡아드리는데 도움을 줄 수는 있겠으나..

    아예 이 시스템 소스 전부에서 문제점이 무엇인가요..이렇게는 시스템을 똑같이 구현할수도 없고..왜 이렇게 했는지 알지도 못하는 상황에서 이게 문제야..라고 말할순 없죠..


    0
  • 달려라코난
    72
    2019-10-25 09:03:18

    네 알겠습니다. 답변감사합니다!!정말 큰 도움이 되었어요.

    0
  • 로그인을 하시면 답변을 등록할 수 있습니다.