StudyServer
web, server, java, spring 등.. 공부한 것을 기록하는 장소 입니다. 공부하면서 정리하였기 때문에 틀린 내용이 있을 수 있습니다. 이야기 해주시면 수정하겠습니다.

Java Coding Conventions

2019-05-17 00:00:00 +0000

Java Coding Conventions

  • 프로젝트 란에 노란 느낌표 있는지 확인(ctrl+o로 정리)
  • Java Conding Convertions는 ctrl+f로 정리 가능
  • logger.info 삭제(자바 로그 정리)
  • System.out.println 삭제
  • 사용안하거나 필요 없는 console.log, alert 삭제
  • 괄호 다음을 띄운다
  • if( 를 if ( 로 수정
  • else if( 를 else if ( 로 수정
  • }else if 를 } else if 로 수정
  • else{ 를 else ( 로 수정
  • }else 를 } else 로 수정
  • for( 를 for ( 로 수정
  • }( 을 ) { 로 수정
    • 검색해서 안띄워져 있으면 띄위기(xml 에도 적용할 것)
  • true, false 는 true는 없애고 false는 ! 로 수정
  • private인데 public으로 접근제어자 사용하지 않았는지 체크!
  • naming 알맞게 짓기
  • 자료형에 기반한 네이밍을 하지 않는다. 예를들어 List userList 였다가 Map 으로 바뀌면 userMap으로 네이밍을 다시 해줘야한다
  • 불필요한 코드나 java대신 xml로 간단하게 처리할 수 있는지 확인하기

Java Long과 long의 차이

2019-05-15 00:00:00 +0000

Long과 long의 차이

  • Long은 Wrapper Class이다. 크기는 8byte (sizeof 함수가 없어서 인터넷에서 찾아봄)
  • long은 순수한 자바 자료형이다. 크기는 8byte
  • Long.SIZE 에 대한 설명 The number of bits used to represent a long value in two’s complement binary form.
  • Wrapper Class는 클래스로써 변환해야할 때, 인자값으로 Class를 받을 때 등.. 의 상황에서 사용한다.
  • 예를들어 클라로부터 a라는 인자를 받을 때도 있고, 받지 않을 때도 있다. 이 때 a의 값이 없을 때는 null이 대입되므로 Wrapper Class를 사용해야한다.
int LongSize = Long.SIZE;
System.out.println("longSize:"+LongSize);

문제 상황

  • Long의 값이 null일 때 long에 Long값을 대입하면 형변환이 되지 않아 에러 발생
  • Long과 long을 혼용해서 사용하고 있었다.. 주의할 것;;

궁금한 것

  • 받는 값이 long 일 때 null을 long으로 변환하지 못해서 형변환 에러가 나서 예외처리가 된다. 이 때 형변환 에러 처리를 하므로 그대로 두는지, WrapperClass로 받은 후 null체크를 한 다음 처리하는지?
  • WrapperClass를 사용한다. 컨트롤 밖보다 안에서 처리를 하도록 한다.

Mybatis 객체 안에 객체 매핑

2019-05-14 00:00:00 +0000

Mybatis 객체 안에 객체 넣기

  • Class 안에 Class를 멤버변수로 가지고 있을 때 Mybatis에서 ResultMap과 association를 사용하여 값을 매핑한다.
  • 현재 TitleVO 클래스 안에 PostReplyVO 클래스가 데이터형인 멤버변수를 가지고 있다

TitleVO.java

@Data
public class TitleVO {
     private long    boardSeq;
     private String  title;
     private String  writer;
     private Date    date;
     private String  dateString;
     private long    count;
     private String  userId;              // userId가 아니라  Name아닌가?
     private long    userSeq;
     private boolean boardDel;
     private PostReplyVO postReplyVO; // 게시물 화면에서 그릴 때  순서 필요
     public PostReplyVO getPostReplyVO() {
           return postReplyVO;
     }
}

PostReplyVO.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PostReplyVO {
     private long    boardSeq;
     private long    parentSeq;
     private int          boardOrder;
     private long    boardDepth;
}

postReply ID 지정

  • TitleVO를 매핑시키는 resultMap을 선언한다.
  • TitleVO의 일반 멤버 변수는 그대로 매핑한다.
  • property에는 멤버변수 이름, column 은 필드 이름을 적는다(as를 사용하여 별명을 적음)
  • TitleVO의 class변수는 association 을 사용해서 매핑한다.
  • association 의 propery는 TitleVO 에서 PostReplyVO 멤버변수 이름, javaType은 매핑할 클래스 이름 PostReplyVO을 적어둔다
<resultMap id="postReply" type="TitleVO">
   <id property="boardSeq" column="boardSeq" />
   <id property="title" column="title" />
   <id property="writer" column="writer" />
   <id property="date" column="date" />
   <id property="count" column="count" />
        <association property="postReplyVO"  javaType="PostReplyVO" >
             <result property="boardSeq"  column="boardSeq" />
             <result property="parentSeq"  column="parentSeq" />
             <result property="boardOrder"  column="boardOrder" />
             <result property="boardDepth"  column="boardDepth" />
        </association>
</resultMap>

쿼리문

  • resultMap을 추가하고 위에서 정의한 ResultMap ID 를 적어준다.
<select id="getTitleVOList" parameterType="SelectVO" resultMap="postReply">
    SELECT board_seq as boardSeq
           , title
           , writer
           , date
           , count
           , user_id as userId
           , user_seq as userSeq
           , board_del as boardDel
           , parent_seq as parentSeq
           , board_order as boardOrder
           , board_depth as boardDepth  
    FROM    board
    
    <include refid="searchBoard"></include>
    ORDER BY parent_seq DESC, board_order ASC
    LIMIT #{start}, #{length}    
</select>

JavaConfig mariadb 연결

2019-05-13 00:00:00 +0000

JavaConfig mariadb 연결

  • pom.xml
  • pom.xml에서 mariadb 라이브러리 추가
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.2</version>
</dependency>

<!-- mybatis-spring -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.1</version>
</dependency>

<dependency>
	<groupId>org.mariadb.jdbc</groupId>
	<artifactId>mariadb-java-client</artifactId>
	<version>2.3.0</version>
</dependency>

JDBC 연결

  • JDBC는 어떤 DB 드라이버든 동일한 코드로 제어할 수 있다.
  • myBatis에 비해 같은 코드 반복이 많고 복잡하다(select 직접 처리) 직접 연결을 열고 닫아줘야 한다
  • 많은 사용자들이 db에 원활하게 접속할 수 있게 db 커넥션풀을 미리 만들어놔야 한다.
  • getConnection(“jdbc:mariadb://접속ip:포트번호/데이터베이스이름”, id, 비밀번호)
  • 아래 코드가 실패하면 비밀번호, db 존재 확인 그 외 1.권한 확인 2. 포트번호 확인 2. 방화벽 확인(ping test)
  • 나는 포트가 올라가있음에도 다른 포트번호와 겹쳐서 그런지(mysql?) mariadb에 접속하지 못했다. 그 후 mariadb 포트번호를 변경하고 접속할 수 있었다.
import static org.junit.Assert.fail;

import java.sql.Connection;
import java.sql.DriverManager;
import org.junit.Test;
import lombok.extern.log4j.Log4j;

@Log4j
public class mysqlDBTest {

	@Test
	public void testConnection() throws ClassNotFoundException {	
		 Class.forName("org.mariadb.jdbc.Driver");
	    try (Connection con = DriverManager.getConnection("jdbc:mariadb://127.0.0.1:3310/test", "test", "1234"))
		{
			System.out.println(con);
		} catch (Exception e) {
			fail(e.getMessage());
		}
	}
}

MyBatis

  • SqlSessionFactory는 DataSource를 참고하여 db와 mybatis를 연결시킴
  • JDBC에서는 DB에 접근할 때마다 Session을 가져와서 사용했지만 Spring에서는 bean에 올려두고 mapper를 이용해서 접근
  • @Mapper, @Service 어노테이션을 이용하면 스프링 빈으로 주입받아 사용 가능설명
  • 자동으로 연결과 종료를 수행한다

  • pom.xml ~~~
org.mybatis mybatis 3.4.6 org.mybatis mybatis-spring 1.3.2 org.springframework spring-tx ${org.springframework-version} org.springframework spring-jdbc 3.0.3.RELEASE

- 아래는 접속을 확인하기 위해서 유닛테스트를 하기 위해 Connection 직접 호출
- RootConfig.java

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration @ComponentScan(basePackages= {“base.toy.vo”}) public class RootConfig {

@Bean
public DataSource dataSource(){
	DriverManagerDataSource dataSource = new DriverManagerDataSource();
	dataSource.setDriverClassName("org.mariadb.jdbc.Driver");
	dataSource.setUrl("jdbc:mariadb://localhost:3310/test");
	dataSource.setUsername("test");
	dataSource.setPassword("1234");
	
	return dataSource;
}

  @Bean
  public SqlSessionFactory sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
    sqlSessionFactory.setDataSource(dataSource());
    return (SqlSessionFactory) sqlSessionFactory.getObject();
  }

}


- test.java

package base.toy.project;

import static org.junit.Assert.*;

import java.sql.Connection;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import base.toy.config.RootConfig; import lombok.Setter; import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {RootConfig.class}) @Log4j public class test {

  @Setter(onMethod_ = { @Autowired })
  private SqlSessionFactory sqlSessionFactory;

  @Test
  public void testMyBatis() {

    try (SqlSession session = sqlSessionFactory.openSession();
       Connection con = session.getConnection();) {
      log.info(con);
      

    } catch (Exception e) {
    	log.info("실패"+e.getMessage());
      fail(e.getMessage());
    }

  } } ~~~

커넥션 풀

  • 여러명의 사용자를 동시에 처리해야하므로 커넥션 풀을 사용해 미리 접속을 만들어두는 것이 좋다.
  • HikariCP 사용
  • pom.xml
<dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
	<version>2.7.4</version>
</dependency>
  • DataSource가 HikariConfig를 사용하는 것으로 변경
  • SqlSession Connect 시 성공
@Bean
public DataSource dataSource(){
	  HikariConfig hikariConfig = new HikariConfig();
	  hikariConfig.setDriverClassName("org.mariadb.jdbc.Driver");
	  hikariConfig.setJdbcUrl("jdbc:mariadb://localhost:3310/test");
	  hikariConfig.setUsername("test");
	  hikariConfig.setPassword("1234");
	
	  HikariDataSource dataSource = new HikariDataSource(hikariConfig);
	return dataSource;
}

DataTable

2019-05-09 00:00:00 +0000

DataTable

  • Jquery PlugIn으로 테이블과 데이터를 연동만 해주시면 페이징, 검색, 정렬 등 기능을 쉽게 사용할 수 있다.
  • DataTable 공식 홈페이지

DataTable 기본 예제

  • server를 사용하지 않고 오로지 client에서 처리하는 예제. 그러므로 페이징, order, search가 server 없이 동작한다
  • 기본예제
  • 아무런 옵션 없이 dataTable을 만들기만 하면 기본테이블이 생성된다

DataTable 주요옵션

  • pageLength: 한 페이지에 보이는 게시물 개수
  • searching : 검색 기능 사용 여부
  • ordering : 게시물 정렬 기능 사용 여부
  • select : 한 페이지에 보일 게시물 개수 지정 기능
  • serverSide: server와 통신여부
  • processing : 서버와 통신 시 응답을 받기 전이라는 ui를 띄울 것인지 여부

데이터 전달

  • 서버에서 데이터를 전달할 때 반드시 ‘data’로 전달해야함.
  • 데이터에 링크를 걸고 싶거나 class를 주고 싶을 뗀 render:function 함수로 데이터를 수정 후 return
columns : [
				{"data":"boardSeq", render:function(data, type, full, meta){
					return '<div class="board-list-boardSeq">'+data+'</div>';
				
				}},
				{"data":"title", render:function(data, type, full, meta){
            		return '<div class="board-list-title">'+data+'</div>';
            	}},
				{"data":"writer"},
				{"data":"dateString"},
				{"data":"count"}
            ],

ajax 사용 시 주의사항

  • dataTable에 대해서 잘 모르고 성공 여부를 알기 위해서 success 함수를 정의했다가 죽는줄 알았다.. dataTable이 적용이 되지 않았는데 이유는 success 함수를 내가 선언했기 때문이었다. dataTable이 성공일 때 테이블을 그려주는 동작을 알아서 한다. 그런데 success 함수를 내가 재정의 해서 그 동작을 지워버린 것이다. 습관대로 success 를 호출하지 말도록 하자.

페이지 무한 없애기

  • 처음 페이징을 적용하면 페이지가 최대 페이지에 상관없이 무한히 생성됨
  • 전체 게시물 개수가 없어서 생기는 현상. 클라이언트에 값을 전달할 때 iTotalRecords, iTotalDisplayRecords 값 전달값이
result.put("iTotalRecords", totalCount);
result.put("iTotalDisplayRecords", totalCount);

페이지 숫자로만 추가

  • …라는 줄임표를 빼고 싶다면 “simple_numbers_no_ellipses” 옵션 사용
  • cdn 추가
    <script type="text/javascript" charset="utf8"
       src="https://cdn.datatables.net/plug-ins/1.10.19/pagination/simple_numbers_no_ellipses.js"></script>
    
  • 옵션추가
    pagingType:"simple_numbers_no_ellipses"
    

검색 구현

  • 검색창에 단어를 입력할 때 마다 검색함.
  • 버튼을 눌러서 데이터를 검색하고싶다면 custom하게 새로 만들어야 함
  • 내가 만든 검색창의 데이터를 서버로 전달하고 싶다면 데이터 전달할 때 id나 class명으로 찾은 요소의 값을 줘야함(찾은 요소와 연결되나봄??)
  d.searchValue = selectValue;    					// x
  d.searchType =  $('.list-select-form').val();		// o
  • 버튼을 누를 때 마다 우의 요소의 값을 새로 넣어줌
  • 그리고 reload 함수 호출
$('#searchBoardButton').click(function(){
	$('.list-select-form').val( $('.list-select-form').val());
	$('.board-list-serach-form').val($('.board-list-serach-form').val());
	$('#board1-table').DataTable().ajax.reload();
});

Source Code

table = $('#board1-table').DataTable({
	pagingType:"simple_numbers_no_ellipses",        // 페이징에서 생략부호 빼기
	serverSide: true,                                             // ajax로 서버 데이터 처리
	pageLength: pageLen,                                          // 게시물 뒤로가기 시 기존 가지고 있던 페이지 크기를 넣어주므로 변수에 값 저장 
	searching: true,
	ordering: false,
	select: true,
	ajax : {
		 "type":"POST",
		 "url":'/board/list',
		 "data":function(d, event){                                   // 서버에 전달할 데이터
			   d.searchValue = $('.board-list-serach-form').val();    // 검색 버튼을 누르면 .board-list-search-form 의 값을 가져옴.
			   d.searchType =  $('.list-select-form').val();
			   var info =  $('#board-list-select-vo').val();
			   if(info != undefined){
					d.start =  ($('#board-list-select-vo .page').val()-1) * 10;
					d.length =  $('#board-list-select-vo .length').val();
					d.searchType =  $('#board-list-select-vo .searchType').val();
					d.searchValue =  $('#board-list-select-vo .searchValue').val();
			   }
			   return d;                    
		 }
		 
	},    
	columns : [                                                       // 서버에서 받은 값을 넣어주기 전 커스텀마이징 할 수 있다.
		 {"data":"boardSeq", render:function(data,  type, full, meta){
			   return '<div  class="board-list-boardSeq">'+data+'</div>';
		 
		 }},
		 {"data":"title", render:function(data,  type, full, meta){
		 return '<div  class="board-list-title">'+data+'</div>';
	}},
		 {"data":"writer"},
		 {"data":"dateString"},
		 {"data":"count"}
],
"initComplete":function(oSettings){                            		// 데이터 테이블 초기화
	var info = $('#board-list-select-vo').val();
	if(info != undefined){
		 var pageNum = $('#board-list-select-vo  .page').val();
			   pageNum *= 1;
			   this.fnPageChange( pageNum-1 );
			   $('#board-list-select-vo').remove();
	}
}
,"fnDrawCallback": function(){                                      // 서버에서 데이터 전달 후 처리
	var api = this.api();
	var json = api.ajax.json().selectVO;
	gLength = json.length;
	gPage = table.page.info().page + 1;
	gStart = json.start;
	gSearchType = json.searchType;
	gSearchValue = json.searchValue;
	
	$('#board1-table').on("click",  ".board-list-title", function(){
		  var parent = $(this).parent();
		 if(undefined == parent){
			   return;
		 }
		 
		 var parent = $(parent).parent();
		 if(undefined == parent){
			   return;
		 }
			   
		 var boardSeq =   $(parent).children('td').children('.board-list-boardSeq').html();
		 if(undefined == $('#boardSearchForm')){
			   return;
		 }
			   
		 var data = {
			   "boardSeq" : boardSeq
		 };
									
		 var url = getHrefBoardList('#actionForm',   '/board/'+boardSeq);
		 ajaxRequest("/board/"+boardSeq,  JSON.stringify(data),  "POST", url);
	});             
}
});

$('#searchBoardButton').click(function(){            				// 검색 버튼 누르기

$('.list-select-form').val( $('.list-select-form').val());			// 값을 전달 할 때 값을 직접참조($('.list-select-form').val();)로 전달하지 않으면 전달값이 갱신이 안됨
$('.board-list-serach-form').val($('.board-list-serach-form').val());
$('#board1-table').DataTable().ajax.reload();						// reload 를 받드시해야한다
	
});

Posts

subscribe via RSS