728x90

https://denodo1.tistory.com/322

 

Unrecognized token 'id': was expecting ('true', 'false' or 'null')

Request Body로 보내지는 JSON의 행방 불명문제api 테스트 시 Postman을 자주 사용하는데, 다음과 같이 JSON을 서버에 보내면,서버의 Request에서 JSON 정보를 찾을 수 없다.해결하지만 동일한 json 데이터를 p

denodo1.tistory.com

 

728x90

Entity 객체를 Builder하여 save시키는 단순한 로직인데 500에러가 발생을 한다고 Fe개발자에게 연락을 받았습니다.

( 회사 실무가 아닌 주말 스터디 팀이에요... ㅎㅎㅎㅎ 500에러 후,,,, 다행 )

바로 aws서버 로그를 까서 보았습니다.

해당 필드에 null값이 들어가면서 sql문에 오류가 발생한 것 인데요.

기존에 잘 쓰던 로직이라 처음에는 뭔가 이해가 되지 않더라구요.

 

바로 최근 변경내역에 대한 히스토리를 보다가 문득 아차싶은 부분을 볼 수 있었습니다.

변경 전 : @GeneratedValue

변경 후 : @GeneratedValue(strategy = GenerationType.IDENTITY)

 

최근 Entity에 대하여 IDENTITY 옵션을 주며 수정을 했습니다.

해당 옵션을 주면서 장애가 발생하게 된 것인데요.

결론부터 말해서 수정방법은 아래와 같습니다.

자동증가(AI)옵션을 추가해주면 됩니다.

 

우리는 개발자이기에 왜 멀쩡하던 기능이 option하나 주었다고 왜 이렇게 장애로 바뀐것 인지 탐구해 볼 필요가 있는데요.

 

yml파일에서 ddl-auto: create로 보통 쓰시진 않을 것 으로 생각합니다.

create 였다면, 해당 테이블을 삭제 후 새로 생성하면서 AI옵션을 넣어주었을 것이고, 물론 장애도 안생겼겠죠.

하지만 update, none으로 설정하였기에 테이블을 새로 생성하진 않습니다.

 

이러면서 문제가 발생 합니다.

기존 @GeneratedValue의 경우 

save() 처리 시 hibernate_sequence테이블에서 id값을 할당받아 자동으로 채우고 insert를 시키게 되는데

변경 된 @GeneratedValue(strategy = GenerationType.IDENTITY)의 경우

save() 처리 시 자동증가값을 사용하도록 설정이 되어 있기에 id값이 sql에 없는걸 확인 할 수 있습니다.

 

눈치 채셨나요? 네 DB와 JPA가 Save()의 sql문이 상이한 것 입니다.

현재 DB는 AI설정이 off이므로 sql에 필수 값 이였던 것 이죠.

 

IDENTITY 외 테이블 방식도 있는데 어떤 DB와 전략을 사용하느냐에 따라 설정하고 DB까지 맞춰줘야겠군요.

 

GeneratedValue설정을 변경 할 때 어떻게 동작하게 될지. DB에 대한 영향도를 생각안하고 변경만 해버린 제 실수가 500장애를 발생시켰습니다.

JPA를 사용하기에 앞서 많은 이해와 영향도를 생각하는 개발자가 되도록 준비해야겠내요.

 

https://kounjeong.tistory.com/20

 

JPA - Field 'id' doesn't have a default value

안녕하세요. Ruk입니다. 요즘 한창 JPA를 탐구하고 사용해보는 중인데요. 이번에 발생한 에러에 대해 적어보려고 해요. Entity 객체를 Builder하여 save시키는 단순한 로직인데 500에러가 발생을 한다고

kounjeong.tistory.com

 

 


JPA 기본키 생성 전략, @GeneratedValue 사용시 주의점

 

JPA로 테이블과 엔티티를 매핑할 때, 식별자로 사용할 필드 위에 @Id 어노테이션을 붙여 테이블의 Primary Key와 연결 시켜줘야한다.

이 때, 컬럼 명을 따로 지정하지 않으면, 관례에 따라 매핑되는 테이블 컬럼명은 camelCase로 작성된 필드명을 snake_case로 바뀐 테이블 컬럼을 찾아서 매핑시켜준다. ex) memberId -> member_id , orderItemId -> order_item_id 

 

@Column 어노테이션을 활용하여 테이블의 pk 컬럼을 따로 지정할 수도 있다.

public class  Member {
    @Id  @Column(name = "member_id") // 컬럼명 따로 지정
    private Long id;
    
    }

 

 

이렇게 @Id로 식별자필드와 테이블의 PK를 매핑만 시켜놓으면, 식별자로 사용될 값을 일일히 수동으로 넣어줘야 하는 불편함이 있는데, @GeneratedValue 를 사용하면 이를 해결할 수 있다.

 

@GeneratedValue 어노테이션을 사용하면 식별자 값을 자동 생성 시켜줄 수 있다.

@GeneratedValue에는 3가지 전략이 있고, JPA에게 전략 선택을 위임하는 옵션인 AUTO 옵션을 포함해, 총 4가지 옵션이 존재한다.

 

1. GenerationType.AUTO 옵션.(자동으로 IDENTITY, SEQUENCE, TABLE 中 택 1)

 

 hibernate.dialect에 설정된 DB 방언 종류에 따라, 하이버네이트가 자동으로 전략을 선택하게끔 위임한다.

@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

*주의 :하이버네이트를 무조건 믿어선 안된다! Mysql의 경우 Auto로 설정하면 당연히 Identity전략을 취할 것이라 생각하고 생략하거나, 추후 DBMS 종류 변경을 고려해 그냥 Auto로 사용하는 경우가 있는데, 버전에 따라 선택되는 전략이 달라질 수 있으므로, 직접 DBMS에 맞는 전략을 지정해주도록 한다.

Mysql일 때 GenerationType.AUTO 에서의 전략 선택 알고리즘(빨간 화살표 흐름)

 

Hibernate 5부터 MySQL에서의 GenerationType.AUTO는 IDENTITY가 아닌 TABLE을 기본 시퀀스 전략으로 가져간다.

참고 : https://jojoldu.tistory.com/295

 

2. IDENTITY 전략

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) 
	private Long id;

- 기본 키 생성을 데이터베이스에 위임한다. 

- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다. (예: MySQL의 AUTO_ INCREMENT)

- IDENTITY 전략은, em.persist()로 객체를 영속화 시키는 시점에 곧바로 insert 쿼리가 DB로 전송되고, 거기서 반환받은 식별자 값을 가지고 1차 캐시에 엔티티를 등록시켜 관리한다.

 

JPA는 보통의 경우에, 트랜잭션이 commit 되는 시점에 쓰기 지연 저장소에 모아놓은 SQL을 한 번에 DB로 전송하며 실행한다. 이렇게 해야 어플리케이션과 DB 사이에 네트워크를 오가는 횟수가 줄어들고 성능면에서 이득을 볼 수 있기 때문이다.

 

하지만 IDENTITY전략은 DB에 기본키 생성을 위임하므로, Mysql의 경우 AUTO_INCREMENT를 활용하여 생성하는데,

이 때, JPA 입장에선 DB에 INSERT SQL를 실행하기 전엔 도저히 AUTO_INCREMENT되는 값을 알 수 없으므로, persist() 시점에 insert 쿼리가 실행되는 것이다. (영속성 컨텍스트로 엔티티를 관리하려면 1차 캐시에 Id값을 key 값으로 들고 있어야 하기 때문에)

 

아래 그림에서 1차 캐시의 Key, Value 값 구조와 쓰기 지연 SQL 저장소와 flush()가 트랜잭션 commit 직전에 이루어지는 순서를 보면 이해가 쉬울 것이다.

 

 

 

3. SEQUENCE 전략

@Entity 
@SequenceGenerator( 
     name = “MEMBER_SEQ_GENERATOR", 
     sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
     initialValue = 1, allocationSize = 50) 
public class Member { 
     @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, 
     generator = "MEMBER_SEQ_GENERATOR") 
     private Long id; 
 
 }

- DB의 시퀀스를 활용하여 Id값을 증가시킨다.

- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트(예: 오라클 시퀀스)

- 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용한다.

 

sequenceName 으로 시퀀스를 분리하여 지정할 수 있고, allocationSize로 한 번에 사용할 시퀀스 덩어리 사이즈를 정해서 최적화 할 수 있다.

 

4. TABLE 전략

 

@Entity 
@TableGenerator(
     name = "MEMBER_SEQ_GENERATOR", 
     table = "MY_SEQUENCES", 
     pkColumnValue = “MEMBER_SEQ", allocationSize = 1) 
public class Member { 
     @Id 
     @GeneratedValue(strategy = GenerationType.TABLE, 
     generator = "MEMBER_SEQ_GENERATOR") 
     private Long id; 
 }

- 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략이다.

- 모든 데이터베이스에 적용 가능하나, 성능적인 손해가 있어서 잘 쓰지 않는다.

 

 

결론 : @GeneratedValue 사용시, 반드시  @GeneratedValue(strategy = GenerationType.전략명) 와 같은 형태로 GenerationType 명시해줘야 한다! 기본값인 AUTO는 못미덥다. 사용하지 말자!

 

JPA와 Mysql 사용시, @GeneratedValue의 GenerationType은 IDENTITY로 설정해서 쓰자!

728x90

 java stream에서 maptoobj 함수는 중간 연산이라 되어 있습니다. 길이 n짜리 배열을 obj object n개로 채우려고 합니다. obj 클래스에는 int 자료형만 하나 있고, 우리는 이 n개의 obj가 깊은 복사가 되어야 해요. 이걸 stream을 써서 할 때, Intstream의 range 메서드와, maptoObj를 사용하면 손쉽게 처리할 수 있습니다.

 


 

 

 mapToObj 메서드를 봅시다. Intstream 뿐만이 아니라, Longstream과 DoubleStream에도 있습니다. 설명을 보면, 스트림으로부터 해당 함수를 적용한 객체 값들의 stream을 반환한다고 되어 있어요. 즉 입력 스트림으로부터 무언가를 받아서, 새로운 결과 가지고 있는 스트림으로 변환합니다.

 

 n개의 크기를 가진 리스트에 깊은 복사한 Obj 오브젝트 n개를 채워야 한다고 해 볼게요. 그러면 단순하게 List를 새로 생성해서 Obj 객체를 추가해도 됩니다. 그런데, 조금 더 생각해 보면 아래와 같은 로직을 생각할 수 있습니다.

 

 

 

 1부터 5까지 있는 stream이 있어요. 이것을 아래와 같이 변환시키면 어떨까요?

 

 

 

 하나의 원소가 새로운 new Obj(3)으로 바뀌는 것입니다. mapToXXX류가 이러한 역할을 수행합니다. 이 경우 IntStream의 원소들을 다른 obj로 변환시키는 apply 작업을 수행했습니다.

 

 

 

 

 그림으로 그리면 이런 상황인 셈입니다. 즉, mapToXXX는 스트림의 요소들을 다른 요소로 바꾼 새로운 스트림을 돌려줍니다. 위 그림에서는 x를 new Obj(3)으로 바꾸었습니다. 따라서, 1 2 3 4 5로 이루어져 있던 것을 new Obj(3), ... , new Obj(3)으로 대체하게 됩니다.

 


 이제 문제 상황을 다시 봅시다. 저는 객체 Obj n개가 채워진 ArrayList를 원합니다. 객체 n개는 깊은 복사가 되어야 하고요. 객체 Obj는 int 필드 하나만 주어져 있습니다. n = 4라고 해 보겠습니다.

 

 

 

 당연하게도, setter가 있기 때문에, 불변 객체가 아닙니다. 어떻게 하면 될까요? 로직부터 설계해 봅시다. 일단, 이전에 배웠던 range는 1부터 n까지의 순서를 가지는 stream을 생성합니다. 고로, IntStream.range(1, 5)를 먼저 수행합니다.

 

 

 

 

 다음에, 해당 stream을 토대로 Obj로 이루어진 스트림을 새로 생성합니다. mapToObj로요.

 

 

 

 그러면, 1부터 5까지 순서로 이루어진 Stream이 new Obj(3)으로 이루어진 4개의 객체로 이루어 집니다. 참고로 각 원소별로 apply가 별개로 동작하므로 매번 새로운 객체가 생기게 됩니다.

 

 

 

 toList로 Stream을 List로 변환합니다.

 

 

 

 최종 코드는 20 ~ 21번째 줄에 나와 있습니다. 23번째 줄에 list의 0번째 원소의 x 값을 2로 바꾸어 봅시다. 만약에 저 객체들이 모두 얕은 복사가 되었다면, 모두 2가 출력되었을 겁니다.

 

 

 

 그런데 3이 출력되었습니다. 이유는 toList가 호출될 때 mapToObj에 있는 apply인 x -> new Obj(3)가 실행되기 때문입니다. 각각의 element에 대해 적용되기 때문에 별개의 새로운 오브젝트 Obj가 생성됩니다.

출처: https://codingdog.tistory.com/entry/java-stream-maptoobj-함수에-대해-알아봅시다 [강아지의 코딩공부:티스토리]

'Java' 카테고리의 다른 글

[Spring Batch] multiple nodes with MySQL 8.0.33  (0) 2024.05.03
자바 어플리케이션 inputstream 주의사항  (1) 2024.04.18
728x90

https://community.letsencrypt.org/t/android-7-0-cant-establish-ssl-connection/152050

 

Android 7.0 can't establish ssl connection

My domain is: starline.ru I ran this command: android 7.0 can't connect It produced this output: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x76a8eb8a00: Failure in SSL library, usually a protocol error error:10000410:SSL

community.letsencrypt.org

 

 

My domain is: starline.ru 4

I ran this command: android 7.0 can't connect

It produced this output:

 
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x76a8eb8a00: Failure in SSL library, usually a protocol error error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:641 0x76a8e2b0a0:0x00000001) error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:800 0x76d2e2c253:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:364)

SSLV3 - wrapper string, actualy sslv3 not use

My web server is (include version):

 
nginx version: nginx/1.14.1
built with OpenSSL 1.1.0f  25 May 2017 (running with OpenSSL 1.1.0l  10 Sep 2019)
TLS SNI support enabled

The operating system my web server runs on is (include version): Debian GNU/Linux 9.13 (stretch)

Previously, the RSA private key was generated. And everything worked.
Now a private key has been generated as EC and application (with okhttp3 https://square.github.io/okhttp 12 or direct use ssl) on Android 7.0 - cannot connect (application using android webclient backend is working).

 Solved by rnz in post #10
 
Once again I regenerated the certificate with the RSA, since ApplePay also had problems.

 

 

You're almost certainly running into this issue:

W…

The only way to fix this is by NOT using an EC certificate. Right now you can work around by switching to P-256, but as the future E1 intermediate is P-384, that's not a long term solution.

 


요약

Android Side 에서 SSL Connection Handshake 가 날 때에 인증에 EC 인증서를 사용하면 안된다는 이야기이다. 즉, 안드로이 측면에서 고치려면 RSA 를 강제하고 인증서를 넘기면 된다.

+ Recent posts