pom.xml 추가 D.I

<!-- spring boot jpa -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- h2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

	<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>
		
		 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

Member Vo 객체

 

package com.example.test;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="testdb") //디비이름써줌
public class MemberDB {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 이 Annotation을 붙여주면 해당 변수를  PrimaryKey로 인식한다.
	private Long id;
	private String name;
	private String zipcode;
	private String email;
	private String active;
	


	public MemberDB(Long id, String name, String zipcode, String email, String active) {
		super();
		this.id = id;
		this.name = name;
		this.zipcode = zipcode;
		this.email = email;
		this.active = active;
	}

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getZipcode() {
		return zipcode;
	}
	public void setZipcode(String zipcode) {
		this.zipcode = zipcode;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getActive() {
		return active;
	}
	public void setActive(String active) {
		this.active = active;
	}
	@Override
	public String toString() {
		return "MemberDB [id=" + id + ", name=" + name + ", zipcode=" + zipcode + ", email=" + email + ", active="
				+ active + "]";
	}
	
	

}

 

jdbc템플릿을 이용하기 위해 member VO의 생성자 유무가 중요하다.

CrudRepository I.O를 상속받아 사용하면 생성자는 protect 생성자이름(){};

만 지정해줘도 자동으로 객체를 할당해준다.

 

위의 @Id,@Entitiy,@table 등의 어노테이션은 이프로젝트는 JPA를 사용하지 않았기에 빼도 아무상관이없다.

package com.example.test;

import java.util.List;

import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository{

	List<MemberDB> findAll();
	
	int save(MemberDB member);
	
	int update(MemberDB member);
	
	int DeleteById(Long id);
	
	List<MemberDB> findById(Long id);
	
}

 레포지터리 인터페이스 

package com.example.test.JDBC;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.example.test.MemberDB;
import com.example.test.MemberRepository;

@Repository
public class JDBCMemberRepository implements MemberRepository {
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	

	
	@Override
	public List<MemberDB> findAll() {
		// TODO Auto-generated method stub
		return jdbcTemplate.query("select * from testdb where active ='TRUE' order by id",(rs,rowNum) -> new MemberDB(
				
				rs.getLong("id"),
				rs.getString("name"),
				rs.getString("zipcode"),
				rs.getString("email"),
				rs.getString("active")
				));
	}

	@Override
	public int save(MemberDB member) {
	
		return jdbcTemplate.update("insert into testdb values(?,?,?,?,default)",
				member.getId(),member.getName(),member.getZipcode(),member.getEmail());
	}

	@Override
	public int update(MemberDB member) {
		// TODO Auto-generated method stub
		return jdbcTemplate.update("update testdb set name = ? where id= ? ",
				member.getName(),member.getId()
				);
	}

	@Override
	public int DeleteById(Long id) {
		// TODO Auto-generated method stub
		return jdbcTemplate.update("update testdb set active = 'FALSE' where id = ?",
				id
				);
	}

	@Override
	public List<MemberDB> findById(Long id,String name) {

		return jdbcTemplate.query("select * from testdb where id =? or name like ?", new Object[] {id,"%"+name+"%"},(rs,rowNum) ->
		
		new MemberDB(	
				rs.getLong("id"),
				rs.getString("name"),
				rs.getString("zipcode"),
				rs.getString("email"),
				rs.getString("active")
				));
	}
	
	

}

JDBC레포지터리 코드

 

스프링 부트서 레포지터리를사용시 반드시 @Repository를 어노테이션해줘야하는걸 잊지말자.

안하면 구동시 오류 생김

 

 

package com.example.test;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping(value ="/members")
@RestController
public class MemberRestAPIController   {
	  @Autowired
	    DataSource dataSource;

	//private MemberRepository memberRepository;

	@Autowired
	private JdbcTemplate jdbcTemplate;
		@PostConstruct 
	    public void run() throws SQLException  {
	        try(Connection connection = dataSource.getConnection()){
	            System.out.println(connection);
	            String URL = connection.getMetaData().getURL();
	            System.out.println(URL);
	            String User = connection.getMetaData().getUserName();
	            System.out.println(User);

	            Statement statement = connection.createStatement();
	            String sql = "CREATE TABLE TESTDB(" +
	                    "ID INTEGER NOT NULL," +
	                    "NAME VARCHAR(255)," +
	                    "ZIPCODE VARCHAR(100),"+
	                    "EMAIL VARCHAR(255),"+
	                    "ACTIVE VARCHAR(10) DEFAULT 'TRUE',"+
	                    "PRIMARY KEY (ID))";
	            //ID INTEGER DEFAULT nextval('seq_id') NOT NULL;
	            //statement.executeUpdate(sql);
	        }

	     // jdbcTemplate.execute("INSERT INTO TESTDB VALUES(10002, '정성훈2','143602','fixblack@gmail.com',default)");
		

	    }
	
	@Autowired
	private MemberRepository memberRepository;

	
	//전체 목록
	@GetMapping
	public List<MemberDB> listAllmemeber(){
		
	    List<MemberDB> allMembers = memberRepository.findAll();
		System.out.println(allMembers);
		return allMembers;
		
	}
	
	//삽입

	@PostMapping
	public ResponseEntity<List<MemberDB>> memberadd(@RequestBody MemberDB member) {
		
		memberRepository.save(member);
		return memberSearch(member.getId());
	}
	
	//수정 @Put
	
	@PutMapping
	public String memberupdate(@RequestBody MemberDB member){//id로
		
		memberRepository.update(member);
		return "redirect:/members";
	}
	
	@DeleteMapping("/{id}")
	public String memberDelete(@PathVariable Long id){
		
		memberRepository.DeleteById(id);
		return "redirect:/members";
	}
	
	//특정 아이디 조회 id or 이름
	
	//조회
	
	@GetMapping("/{id}")
	public ResponseEntity<List<MemberDB>> memberSearch(@PathVariable Long id) { //@PathVariable
		
		 List<MemberDB> searchMember = memberRepository.findById(id);
		System.out.println(searchMember);
		return new ResponseEntity<List<MemberDB>>(searchMember,HttpStatus.OK);
		
	}
	
}
	

    

 

 

위의 주석은 초기에 CrudRepository를 활용해서 코드를 작성하려했지만 FindById쪽이 잘 구현되지않아

JDBC를 사용하는것으로 방향을 선회했다. 하는법은 나중에 코드를 더분석하고 해결해 봐야할듯 하다.

 

 

[Spring] ResponseEntity는 왜 쓰는 것이며 어떻게 쓰는걸까?

ResponseEntity, @ResponseBody Client 의 플랫폼에 구애받지 않는 독립적인 RestFul API를 개발하기 위해, 상태코드, HttpHeader, 응답메시지, 반환 데이터를 모두 지정해서 반환해주기 위해 사용하는 것이다. Cl..

a1010100z.tistory.com

RestFulAPI를 구현하려면 ResPonseEntity사용을 권한다. 그이유는 위의 링크서 확인이 가능하다.

 

spring.datasource.hikari.maximum-pool-sie=4
spring.datasource.url=jdbc:postgresql://192.168.11.222:5432/jshdb
spring.datasource.username=jsh
spring.datasource.password=1234

application.roperties의 코드이다.

여기서 주의해야할점은 url주소 지정인데

 

postgresql과 연동하기위해 docker초기화면서 보여지는 ip주소와 뒤에 5432는 컨테이너를 생성할때 할당하는 포트번호이다 

뒤는 연동할 DB이름을 적어주면된다.

 

postman프로그램을 사용하여 JSON형태로 값을 send하여 확인하는 화면

 

RESTful API를 사용하면 어느 클라이언트,운영체에 상관없이 동일한 시스템을 구성 할 수 가있다.

그 만큼 유지보수 및 수정이 훨씬 수월해지고 익숙해지면 개발자에게 참좋은 방식이라 생각된다.

 

 

추가

   public void run(ApplicationArguments args) throws Exception {
	        try(Connection connection = dataSource.getConnection()){
	            System.out.println(connection);
	            String URL = connection.getMetaData().getURL();
	            System.out.println(URL);
	            String User = connection.getMetaData().getUserName();
	            System.out.println(User);

	            Statement statement = connection.createStatement();
	            String sql = "CREATE TABLE TESTDB1(" +
	                    "ID INTEGER NOT NULL," +
	                    "NAME VARCHAR(255)," +
	                    "ZIPCODE VARCHAR(100),"+
	                    "EMAIL VARCHAR(255),"+
	                    "ACTIVE VARCHAR(10) DEFAULT 'TRUE',"+
	                    "PRIMARY KEY (ID))";
	            //ID INTEGER DEFAULT nextval('seq_id') NOT NULL;
	            statement.executeUpdate(sql);
	        }

스프링 부트를 통해서 직접 테이블을 생성하고 싶을때 쓰는 구문이다.

클래스에 implements ApplicationRunner를 붙여주고 run함수는 오버라이드 해주면 정상적으로 작동한다.

 

Construcotr(생성자)

public  class  ExampleCase{

	private  final  ChocolateService  chocolateService;
	
	private  final  DrinkService  drinkService;

	@Autowired
	public  ExampleCase(ChocolateService  chocolateService, DrinkService  drinkService){
		this.chocolateService = chocolateService;
		this.drinkService = drinkService;
	}
}

Setter

public  class  ExampleCase{
	private  ChocolateService  chocolateService;

	private  DrinkService  drinkService;

	@Autowired
	public  void  setChocolateService(ChocolateService  chocolateService){
		this.chocolateService = chocolateService;
	}

	@Autowired
	public  void  setDrinkService(DrinkService  drinkService){
		this.drinkService = drinkService;
	}

}

 Field

 

public  class  ExampleCase{

	@Autowired
	private  ChocolateService  chocolateService;

	@Autowired
	private  DrinkService  drinkService;
}

3개의 코드를보면 Field Injection이 심플하고 간편해보인다. 하지만 Spring 4.3부터는 대부분 Contructor Inject을 추천한다.

 

Field Injection을 추천하지않는 이유

 

 

단일 책임의 원칙 위반

 의존성을 주입하기가 쉽다. @Autowired 선언 아래 여러개를 막 추가 할 수가 있다. 하지만 Constructor Injection을 사용하면 다른 Injection 타입에 비해 위기감을 느끼게 해준다. Constructor의 파라미터가 많아짐과 동시에 하나의 클래스가 많은 책임을 떠안는다는 걸 알 수 있다. 이러한 징조들이 리팩토링을 해야한다는 신호가 될 수 있다.

 

의존성이 숨는다.

 DI(Dependecy Injection) 컨테이너를 사용한다는 것은 클래스가 자신의 의존성만 책임진다는게 아니다. 제공된 의존성 또한 책임진다. 그래서 클래스가 어떤 의존성을 책임지지 않을 때, 메서드나 생성자를 통해 (Setter나 Construcotr)이 확t실하게 커뮤니케이션이 되어야한다. 하지만 Field Injection은 숨은 의존성만 제공해준다.

 

DI 컨테이너의 결합성과 테스트 용이성

DI 프레임워크의 핵심 아이디어는 관리되는 클래스가 DI컨테이너에 의존성이 없어야 한다. 즉, 필요한 의존성을 전달하면 독립적으로 인스턴스화 할 수 있는 단순 POJO여야 한다. DI컨테이너 없이도 유닛테스트에서 인스턴스화 시킬 수 있고, 각각 나누어서 테스트도 할 수있다. 컨테이너의 결합성이 없다면 관리하거나 관리하지 않는 클래스를 사용할 수있고, 심지어 다른 DI컨테이너로 전환 할 수있다. 

 

 

불변성(Immutability)
Constructor Injection과 다르게 Field Injection은 final을 선언할 수 없다. 그래서 객체가 변할 수 있다.

순환 의존성
Constructor Injection에서 순환 의존성을 가질 경우 BeanCurrentlyCreationExeption을 발생시킴으로써 순환 의존성을 알 수 있다.

  • 순환 의존성이란? First Class가 Second Class를 참조하는데 Second Class가 다시 First Class를 참조할 경우 혹은 First Class가 Second Class를 참조하고, Second Class가 Third Class를 참조하고 Third Class가 First Class를 참조하는 경우 이를 순환 의존성이라고 부른다.

 

그럼 Setter Injection 과 Constructor Injection중에서는 어떤걸 추천할까?

 

Setter injection

Setter Injection은 선택적인 의존성을 사용할 때 유용하다. 상황에 따라 의존성 주입이 가능하다. 스프링 3.x버전에서는 Setter Injection을 추천했었다.

 

Constructor Injection

Constructor Injection은 필수적인 의존성 주입에 유용하다. 그리고 final을 선언 할 수 있으므로 객체가 불변 하도록 할 수있다. 그리고 순환 의존성 여부도 파악할수 있다. 그래서 이 코드가 나쁜 디자인 패턴이여서 리팩토링 해야하는지 아닌지 여부도 판단이 가능하다.

 

스프링 4.3버전 부터는 클래스를 DI프레임워크로부터 분리할 수있다. 단일 생성자에 한해 @Autowired를 붙이지 않아도 된다. 이러한 장점 때문에 스프링 4.x버전서는 Setter Injection이아닌 Constructor Injection을 권장한다. 

 

굳이 Setter Injection을 사용한다면, 합리적인 디폴트를 부여 할 수있고 선택적인 의존성을 사용할 때만 사용해야한다고 말한다.

그렇지 않으면 not-null체크 의존성을 사용하는 모든 코드에 구현해야한다.

 

 

 

출처:https://zorba91.tistory.com/238

+ Recent posts