JPA
properties
# DB Connection
spring.datasource.dbcp2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.99.100:3306/mydb
spring.datasource.username=scott
spring.datasource.password=tiger
# jpa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
# logging slf4j
logging.level.web=info
logging.level.com.bit.boot06=debug
spring.jpa.hibernate.ddl-auto=?
create : 기존테이블 삭제 후 다시 생성
create-drop: create와 같으나 종료시점에 테이블 DROP
update: 변경분만 반영
validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
none: 사용하지 않음
Entity
import ...;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Getter
@Entity
@Table(name = "dept03")
public class Dept03 {
@Id // pk
// @GeneratedValue(strategy = GenerationType.AUTO) // auto_increment
private long deptno;
// @Column(name = "domainname", columnDefinition = "varchar(8) default '제목없음'", nullable = false)
@Column(columnDefinition = "varchar(8)")
private String dname;
// @Column(name = "location", columnDefinition = "text not null")
private String loc;
public ResponseDeptVo getEntity() {
return ResponseDeptVo.builder()
.deptno(deptno)
.dname(dname)
.loc(loc).build();
}
}
DTO - Entity의 영속성을 지키기 위해서 Request, Response DTO를 정의해서 사용한다.
import ...;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RequestDeptVo {
private long deptno;
private String dname;
private String loc;
}
Repository
JpaRepository를 상속받기만 해도 기본적인 CRUD와 관련된 메서드들이 구현되어있으며 바로 사용 할 수 있다.
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.bit.boot06.domain.entity.Dept03;
public interface DeptRepo extends JpaRepository<Dept03, Long>{
List<Dept03> findAll(Sort sort);
List<Dept03> findAllByOrderByDeptnoDesc();
List<Dept03> findAllByOrderByDnameDesc();
Dept03 findByDname(String string);
List<Dept03> findByDname(String dname, Sort sort);
Dept03 findByDnameContaining(String dname);
@Query(value = "select * from dept03 a", nativeQuery = true)
List<Dept03> myall();
}
Entity Test (Junit 5 jupiter)
import ...;
@SpringBootTest
public class DeptRepoTest {
@Autowired
private DeptRepo deptRepo;
@Test
public void test() {
Dept03 entity = Dept03.builder()
.deptno(1111)
.dname("teset")
.loc("test")
.build();
deptRepo.save(entity);
}
}
Service
import ...;
@Service
@AllArgsConstructor
public class DeptService {
private final DeptRepo deptRepo;
ResponseDeptVo insertOne(RequestDeptVo bean) {
Dept03 entity = Dept03.builder()
.deptno(bean.getDeptno())
.dname(bean.getDname())
.loc(bean.getLoc())
.build();
return deptRepo.save(entity).getEntity();
}
ResponseDeptVo selectOne(long deptno) {
// return deptRepo.findById(deptno).get().getEntity();
// return deptRepo.findByDname("tester1").getEntity();
return deptRepo.findByDnameContaining("1").getEntity();
}
ResponseDeptVo updateOne(RequestDeptVo bean) {
Optional<Dept03> op = deptRepo.findById(bean.getDeptno());
if(op.isEmpty()) throw new RuntimeException("존재하지 않음");
return deptRepo.save(Dept03.builder()
.deptno(op.get().getDeptno())
.dname(bean.getDname())
.loc(bean.getLoc())
.build()).getEntity();
}
void deleteOne(long deptno) {
Optional<Dept03> op = deptRepo.findById(deptno);
if(op.isEmpty()) throw new RuntimeException("존재하지 않음");
// deptRepo.deleteById(deptno);
deptRepo.delete(op.get());
}
List<ResponseDeptVo> selectAll() {
List<ResponseDeptVo> list = new ArrayList<>();
Sort sort = Sort.by("deptno").descending();
// deptRepo.findAllOrderByIdDesc().forEach(bean->list.add(bean.getEntity()));
// for(Dept03 bean : deptRepo.findAllByOrderByDeptnoDesc()) {
for(Dept03 bean : deptRepo.findAll(sort)) {
// list.add(bean.getEntity());
list.add(ResponseDeptVo.builder()
.deptno(bean.getDeptno())
.dname(bean.getDname())
.loc(bean.getLoc())
.build());
}
return list;
}
List<ResponseDeptVo> selectAll(int pageNum, int limit){
List<ResponseDeptVo> list = new ArrayList<>();
Sort sort = Sort.by("deptno").descending();
Pageable page = PageRequest.of(pageNum, limit, sort);
// Pageable page = Pageable.ofSize(limit).withPage(pageNum);
deptRepo.findAll(page).forEach(ele->list.add(ele.getEntity()));;
return list;
}
List<ResponseDeptVo> myall() {
List<ResponseDeptVo> list = new ArrayList<>();
deptRepo.myall().forEach(bean->list.add(bean.getEntity()));
return list;
}
}
Service Test (Junit 5 jupiter)
import ...;
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class DeptServiceTest {
@Autowired
DeptService deptService;
RequestDeptVo target ;
@BeforeEach
public void before() {
target = RequestDeptVo.builder()
.deptno(7777)
.dname("tester7")
.loc("test")
.build();
}
@Test
@Transactional
@Commit
@Order(1)
void test1InsertOne() {
// assertNotNull(deptService);
ResponseDeptVo result = deptService.insertOne(RequestDeptVo.builder()
.deptno(target.getDeptno())
.dname(target.getDname())
.loc(target.getLoc())
.build());
log.debug(result.toString());
assertEquals(target.getDeptno(), result.getDeptno());
assertEquals(target.getDname(), result.getDname());
assertEquals(target.getLoc(), result.getLoc());
}
@Test
@Order(2)
public void test2SelectOne() {
ResponseDeptVo result = deptService.selectOne(target.getDeptno());
log.debug(result.toString());
assertEquals(target.getDeptno(), result.getDeptno());
assertEquals(target.getDname(), result.getDname());
assertEquals(target.getLoc(), result.getLoc());
}
@Test
@Order(3)
public void test3UpdateOne() {
target.setDname("한글");
ResponseDeptVo result = deptService.updateOne(target);
log.debug(result.toString());
assertEquals(target.getDeptno(), result.getDeptno());
assertEquals(target.getDname(), result.getDname());
assertEquals(target.getLoc(), result.getLoc());
}
@Test
@Order(4)
public void test4DeleteOne() {
deptService.deleteOne(target.getDeptno());
}
@Test
@Order(5)
public void test5SelectAll() {
for(ResponseDeptVo bean : deptService.selectAll()) {
System.out.println(bean.toString());
}
}
@Test
public void test6SelectPaging() {
for(ResponseDeptVo bean : deptService.selectAll(0,5)) {
System.out.println(bean);
}
}
@Test
public void test7MyAll() {
for(ResponseDeptVo bean : deptService.myall()) {
System.out.println(bean);
}
}
}
WebSocket
EchoHandler
import ...;
public class EchoHandler extends TextWebSocketHandler {
private static Map<String, WebSocketSession> list = new HashMap<>();
public static Map<String, WebSocketSession> getList() {
return list;
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("종료...");
list.remove(session.getAttributes().get("sid"));
}
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("접속됨...");
System.out.println("websocket session ID:" + session.getId());
// for(Entry<String,Object> entry :session.getAttributes().entrySet()) {
// System.out.println(entry.getKey() + ":" + entry.getValue());
// }
list.put((String)session.getAttributes().get("sid"), session);
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
System.out.println("메세지수신... " + message);
for(WebSocketSession sock : list.values()) {
sock.sendMessage(message);
}
}
}
Controller
package com.bit.boot07;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import com.bit.boot07.config.EchoHandler;
import lombok.AllArgsConstructor;
@Controller
@AllArgsConstructor
public class RootController {
final WebSocketHandler webSocketHandler;
@GetMapping("/ex01")
public String ex01(HttpSession session, String id) throws Exception {
session.setAttribute("user", "admin");
session.setAttribute("sid", session.getId());
return "ex01";
}
@GetMapping("/ex02")
public String ex02(HttpSession session) throws IOException {
System.out.println("Controller session ID:"+session.getId());
System.out.println(webSocketHandler);
Map<String, WebSocketSession> list = ((EchoHandler)webSocketHandler).getList();
list.get(session.getId()).sendMessage(new TextMessage("서버에서 보낸 메세지"));
return "ex02";
}
@GetMapping("/ex03")
public String ex03(HttpSession session) {
session.invalidate();
return "ex03";
}
}
View - index, ex01 등 전부 동일
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
</head>
<body>
<h1>Index Page</h1>
<div>
<input/>
<button>전송</button>
</div>
<ul></ul>
<script type="text/javascript">
// WebSocket 연결 생성
const socket = new WebSocket('ws://localhost:8080/echo');
// 연결이 열리면
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
console.log(socket, event);
});
// 연결이 닫히면
socket.addEventListener('close', function(e){
setTimeout(function() {
// 재접속
socket = new WebSocket('ws://localhost:8080/echo')
}, 1000);
})
// 메시지 수신
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
$('<li/>').text(event.data).appendTo('ul');
});
$(function() {
$('button').click(function(){
let msg = $('input').val();
socket.send(msg);
$('input').val('');
});
});
</script>
</body>
</html>
SocketConfig
import ...;
@Configuration
@EnableWebSocket
public class SocketConfig implements WebSocketConfigurer{
@Bean
WebSocketHandler getWebSocketHandler() {
return new EchoHandler();
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
WebSocketHandlerRegistration reg = registry.addHandler(getWebSocketHandler(), "/echo");
reg.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
Sockjs
EchoHandler는 위에서 작성한 것 그대로 재활용
WebSocketConfig
package com.bit.boot08.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
@Bean
EchoHandler getEchoHandler() {
return new EchoHandler();
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(getEchoHandler(), "/echo")
.withSockJS();
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
</head>
<body>
<h1>index page</h1>
<script type="text/javascript">
var sock = new SockJS('/echo');
sock.onopen = function() {
console.log('open');
sock.send('test');
};
sock.onmessage = function(e) {
console.log(e); // data, timestamp
//sock.close();
};
sock.onclose = function() {
console.log('close');
};
</script>
</body>
</html>
Spring Websocket
HelloMessage & Greeting
package com.bit.boot08;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HelloMessage {
private String cmd;
private String name;
}
---------------------------------
package com.bit.boot08;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
private String content;
}
GreetingController
package com.bit.boot08;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
// public Greeting greeting(String message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
// return new Greeting("Hello, " + message + "!");
}
}
WebSocketConfig
package com.bit.boot08.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"
integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"
integrity="sha512-iKDtgDyTHjAitUDdLljGhenhPwrbBfqTKWO1mkhSFH3A7blITC9MhYon6SjnMhp4o0rADGw9yAC6EW4t5a4K3g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<h1>index page</h1>
<noscript>
<h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2>
</noscript>
<div id="main-content" class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket connection:</label>
<button id="connect" class="btn btn-default" type="submit">Connect</button>
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
</button>
</div>
</form>
</div>
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="name">What is your name?</label>
<input type="text" id="name" class="form-control" placeholder="Your name here...">
</div>
<button id="send" class="btn btn-default" type="submit">Send</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody id="greetings">
</tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}
// 접속
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 수신 Listener Event - 큐스택에 쌓여있는 데이터를 불러오도록 정기적으로 새로고침
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
// 접속종료
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
// 메세지 보내기
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({cmd:'귓속말', name: $("#name").val()}));
//stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$("#connect").click(function () {connect();});
$("#disconnect").click(function () {disconnect();});
$("#send").click(function () {sendName();});
});
</script>
</body>
</html>
'회고록(TIL&WIL)' 카테고리의 다른 글
2023.03.20~2023.03.21 SVN - Jenkins Windows 로컬 서버 CI/CD (0) | 2023.03.21 |
---|---|
TIL 2023.03.21 암호화, JWT, Spring Security (0) | 2023.03.21 |
TIL 2023.03.17 React 2 (xhr, fetch, axios, CRUD, JPA) (0) | 2023.03.17 |
TIL 2023.03.16 React (기본문법 jsx, useState, react-router) (0) | 2023.03.16 |
TIL 2023.03.15 Node.js 3 (Docker-network, Express-Generator, Express-session, JS-ES6, React) (0) | 2023.03.15 |