728x90

6.6. Subtraction Class

We can use subtraction to negate one or more character classes. For example, we can match a set of odd decimal numbers:

@Test
public void givenSetWithSubtraction_whenMatchesAccurately_thenCorrect() {
    int matches = runTest("[0-9&&[^2468]]", "123456789");
 
    assertEquals(matches, 5);
}Copy

Only 1, 3, 5, 7, 9 will be matched.

 

 

Let's dive to Pattern and Matcher

1.

if String qr has contains "substring value" like otpauth%3A%2F%2Ftotp%2F4009acd5fe30716b6201a93f5ed98999%253A35mwlee%2540naver.com%3Fsecret%3DMIHON7TZY7SEIH27RAOROQ7DI7LHCAJEDBLI4R6LSITWZSITS7PQN75S65E2TLEB%26issuer%3D%25EB%258D%2594%25EB%2593%25AC%25EC%259D%25B4%25EC%2599%2595%25ED%2594%25BC%25EC%25BD%259C%25EB%25A1%259C%26id%3Do87a66240433494aa362ad88e69af9c5

 

We can parse ID string like this

    try{
        String decodeStr = URLDecoder.decode(qr, "UTF-8"); // URL Decode
        Pattern pattern = Pattern.compile("id=([^&]+)"); // Standby ID substring
        Matcher matcher = pattern.matcher(decodeStr);	// Matcher Object will find the target data

        if(matcher.find()) {
            jsonUuid = matcher.group(1);
            log.debug("Extracted ID : {}", jsonUuid);
        } else {
            log.debug("ID not found");
        }
    } catch (UnsupportedEncodingException e){
        e.printStackTrace();;
    }

 

 

https://www.baeldung.com/regular-expressions-java

 

728x90

https://kdhyo98.tistory.com/48

 

[Java] Random보단 SecureRandom 를 사용하자.

🙄서론 Random을 사용하다가 소나큐브에서 Critical 버그가 발생했다. 그 후 알아본 내용을 정리해본다. 😉본론 - 에러 메세지 (번역은 구글번역을 통해 되었기 때문에 오역이 있을 수 있습니다..) "

kdhyo98.tistory.com

https://www.baeldung.com/java-generate-random-long-float-integer-double

 

    public static long generatedRandomLongBounded(){
        SecureRandom random = new SecureRandom();
        return Math.abs(random.nextLong());
    }

 

 

728x90

목차

1. 개요

MySQL에는 아래 3가지 방법을 이용하여 중복 레코드를 관리할 수 있다.

  1. INSERT IGNORE ...
  2. REPLACE INTO ...
  3. INSERT INTO ... ON DUPLICATE UPDATE

각 방법의 특징을 요약하면 다음과 같다.

방법특징

INSERT IGNORE ... 최초 입수된 레코드가 남아 있음
최초 입수된 레코드의 AUTO_INCREMENT 값은 변하지 않음
REPLACE INTO ... 최초 입수된 레코드가 삭제되고, 신규 레코드가 INSERT됨
AUTO_INCREMENT의 값이 변경됨
INSERT INTO ... ON DUPLICATE UPDATE INSERT IGNORE의 장점 포함함
중복 키 오류 발생 시, 사용자가 UPDATE될 값을 지정할 수 있음

2. 사전 조건

중복 레코드 관리를 위해선 테이블에 PRIMARY KEY 혹은 UNIQUE INDEX가 필요하다.

본 예제에서는 아래와 같은 person 테이블을 이용하여 설명한다.

CREATE TABLE person
(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(20),
  address VARCHAR(255),
  PRIMARY KEY (id),
  UNIQUE INDEX (name) -- 중복 검사용 필드
);

person 테이블에서 PRIMARY KEY로 지정된 id 필드는 AUTO_INCREMENT를 위해 만든 필드이며, 본 문서에는 name 필드를 이용하여 중복을 검사한다.

 

만약 기존에 만들어진 테이블에 PRIMARY KEY 혹은 UNIQUE INDEX를 추가하려면 아래의 SQL을 이용하면 된다.

-- PRIMARY 추가하는 방법
ALTER TABLE person ADD PRIMARY KEY (name)

-- UNIQUE INDEX를 추가하는 방법
ALTER TABLE person ADD UNIQUE INDEX (name)

3. 중복 처리 방법

3-1) INSERT IGNORE

INSERT IGNORE는 중복 키 에러가 발생했을 때 신규로 입력되는 레코드를 무시하는 단순한 방법이다.

다음의 예를 보면 중복 키 에러가 발생했을 때 INSERT 구문 자체는 오류가 발생하지 않고, 대신’0 row affected’가 출력된 것을 볼 수 있다.

mysql> INSERT IGNORE INTO person VALUES (NULL, 'James', 'Seoul');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT IGNORE INTO person VALUES (NULL, 'Cynthia', 'Yongin');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT IGNORE INTO person VALUES (NULL, 'James', 'Seongnam');
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT IGNORE INTO person VALUES (NULL, 'Cynthia', 'Seoul');
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT IGNORE INTO person VALUES (NULL, 'James', 'Incheon');
Query OK, 0 rows affected (0.00 sec)

SELECT의 결과는 2건만 존재한다.

mysql> SELECT * FROM person;
+----+---------+---------+
| id | name    | address |
+----+---------+---------+
|  1 | James   | Seoul   |
|  2 | Cynthia | Yongin  |
+----+---------+---------+
2 rows in set (0.00 sec)

James의 주소가 최초 입력한 Seoul이다. 즉, 최초에 입력된 레코드가 남아 있는 걸을 볼 수 있다.

또한 AUTO_INCREMENT 컬럼의 값이 1, 2인 것에 주목하라.

MySQL에서 AUTO_INCREMENT는 식별키 용도로 많이 사용하는데, 중복 발생 여부에 따라 식별 키가 변경되는 경우 여러 가지 불편한 점이 생긴다.

INSERT IGNORE에서는 AUTO_INCREMENT의 값이 변경되지 않는다는 장점이 있다.

3-2) REPLACE INTO

REPLACE INTO는 중복이 발생되었을 때 기존 레코드를 삭제하고 신규 레코드를 INSERT하는 방식이다.

person 테이블을 drop 후 다시 생성한 뒤에 아래의 레코드를 입수해보자.

mysql> REPLACE INTO person VALUES (NULL, 'James', 'Seoul');
Query OK, 1 row affected (0.00 sec)

mysql> REPLACE INTO person VALUES (NULL, 'Cynthia', 'Yongin');
Query OK, 1 row affected (0.00 sec)

mysql> REPLACE INTO person VALUES (NULL, 'James', 'Seongnam');
Query OK, 2 rows affected (0.00 sec)

mysql> REPLACE INTO person VALUES (NULL, 'Cynthia', 'Seoul');
Query OK, 2 rows affected (0.00 sec)

mysql> REPLACE INTO person VALUES (NULL, 'James', 'Incheon');
Query OK, 2 rows affected (0.00 sec)

세번째 레코드를 입수할 때부터는 ‘2 rows affected’가 출력되었다. (INSERT IGNORE에서는 ‘0 rows affected’가 출력되었었다)

 
mysql> SELECT * FROM person;
+----+---------+---------+
| id | name    | address |
+----+---------+---------+
|  4 | Cynthia | Seoul   |
|  5 | James   | Incheon |
+----+---------+---------+
2 rows in set (0.00 sec)

id가 4, 5로 변하였다. 또한 James의 주소가 “Incheon”으로 변경되었다.

이를 ‘2 rows affected’와 함께 종합적으로 판단한다면 “REPLACE INTO”는 다음과 같이 작동하는 것을 알 수 있다.

  • 중복 키 오류 발생 시 기존 레코드를 삭제 -> 첫 번째 레코드가 affected되었음
  • 이후 새로운 레코드를 입력 -> 두 번째 레코드가 affected되었음

그래서 ‘2 rows affected’가 출력되었다.

새로운 레코드가 입력되면서 AUTO_INCREMENT 컬럼의 값이 매번 새롭게 발급되었다.

“REPLACE INTO”는 그다지 좋은 방법이 아닌데 앞서 이야기한 것처럼 AUTO_INCREMENT는 레코드를 식별할 수 있는 id로 사용되는데 중복 발생 시 id가 변경되기 때문이다.

3-3) ON DUPLICATE UPDATE

ON DUPLICATE UPDATE의 장점은 중복 키 오류 발생 시 사용자가 원하는 값을 직접 설정할 수 있다는 점이다.

우선 기본적인 사용 방법을 보자.

마찬가지로 테이블을 drop후 새로 생성했다.

mysql> INSERT INTO person VALUES (NULL, 'James', 'Seoul')
           ON DUPLICATE KEY UPDATE address = VALUES(address);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'Cynthia', 'Yongin')
           ON DUPLICATE KEY UPDATE address = VALUES(address);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'James', 'Seongnam')
           ON DUPLICATE KEY UPDATE address = VALUES(address);
Query OK, 2 rows affected, 1 warning (0.01 sec)

mysql> INSERT INTO person VALUES (NULL, 'Cynthia', 'Seoul')
           ON DUPLICATE KEY UPDATE address = VALUES(address);
Query OK, 2 rows affected, 1 warning (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'James', 'Incheon')
           ON DUPLICATE KEY UPDATE address = VALUES(address);
Query OK, 2 rows affected, 1 warning (0.01 sec)

(‘2 rows affected’의 의미는 아래 내용을 읽고 각자 생각해보자)

SELECT 결과를 보자.

mysql> SELECT * FROM person;
+----+---------+---------+
| id | name    | address |
+----+---------+---------+
|  1 | James   | Incheon |
|  2 | Cynthia | Seoul   |
+----+---------+---------+
2 rows in set (0.00 sec)

이번에는 id 값이 최초 입수된 레코드의 값 그대로이다. 하지만 address의 값이 마지막에 입수한 레코드로 변경되어 있다.

 

INSERT INTO ... ON DUPLICATE KEY UPDATE의 장점은 중복 발생 시 필드의 값을 내 맘대로 UPDATE할 수 있다는 점이다.

id 필드만 놓고 보면 INSERT IGNORE와 동일하지만 address의 값이 변경된 것이 INSERT IGNORE와 INSERT INTO ... ON DUPLICATE KEY UPDATE의 차이점이다.

ON DUPLICATE KEY UPDATE를 이용하면 재미있는 것들을 할 수 있다. 중복 레코드가 총 몇 번이나 입수되었는지를 기록해보자.

이를 위해 inserted_cnt 필드를 추가하였다.

CREATE TABLE person
(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(20),
  address VARCHAR(255),
  inserted_cnt INT, -- 레코드가 몇 번 입수되었는지 확인용 필드
  PRIMARY KEY (id),
  UNIQUE INDEX (name)
);
mysql> INSERT INTO person VALUES (NULL, 'James', 'Seoul', 1)
           ON DUPLICATE KEY UPDATE inserted_cnt = inserted_cnt + 1;
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'Cynthia', 'Yongin', 1)
           ON DUPLICATE KEY UPDATE inserted_cnt = inserted_cnt + 1;
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'James', 'Seongnam', 1)
           ON DUPLICATE KEY UPDATE inserted_cnt = inserted_cnt + 1;
Query OK, 2 rows affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'Cynthia', 'Seoul', 1)
           ON DUPLICATE KEY UPDATE inserted_cnt = inserted_cnt + 1;
Query OK, 2 rows affected (0.00 sec)

mysql> INSERT INTO person VALUES (NULL, 'James', 'Incheon', 1)
           ON DUPLICATE KEY UPDATE inserted_cnt = inserted_cnt + 1;
Query OK, 2 rows affected (0.00 sec)

SELECT를 해 보면 inserted_cnt에는 해당 중복 값이 몇 번 INSERT 시도가 되었는지 기록되어 있을 것이다.

mysql> SELECT * FROM person;
+----+---------+---------+--------------+
| id | name    | address | inserted_cnt |
+----+---------+---------+--------------+
|  1 | James   | Seoul   |            3 |
|  2 | Cynthia | Yongin  |            2 |
+----+---------+---------+--------------+
2 rows in set (0.00 sec)

inserted_cnt 필드의 값을 보면 알겠지만, 레코드가 몇 번 입수되었는지 저장되어 있다.

주의해야 할 점은 새로온 레코드의 값으로 UPDATE하고자 할 때는 항상 “VALUES(column)”과 같이 VALUES()로 감싸야 한다는 점이다. 만약 다음과 같이 VALUES()` 없이 필드명만 사용하는 것은 기존 레코드의 컬럼 값을 의미하게 된다.

INSERT INTO person VALUES (NULL, 'James', 'Incheon')
    ON DUPLICATE KEY UPDATE address = address;

따라서 address의 값이 UPDATE되지 않는다.

 

 

Origin

https://jason-heo.github.io/mysql/2014/03/05/manage-dup-key2.html

 

MySQL 중복 레코드 관리 방법 (INSERT 시 중복 키 관리 방법 (INSERT IGNORE, REPLACE INTO, ON DUPLICATE UPDATE))

Test에 사용된 MySQL 버전 목차 1. 개요 MySQL에는 아래 3가지 방법을 이용하여 중복 레코드를 관리할 수 있다. INSERT IGNORE ... REPLACE INTO ... INSERT INTO ... ON DUPLICATE UPDATE 각 방법의 특징을 요약하면 다음

jason-heo.github.io

 

728x90

Issue

Cannot change the ExecutorType when there is an existing transaction in _____Processor.

 

 

1. Set a Consistent ExecutorType

Ensure that the ExecutorType is consistently set across all MyBatis operations. Typically, ExecutorType.BATCH is used for batch processing, but you can choose the appropriate one for your use case (SIMPLE, REUSE, BATCH).

 

In runtime, Spring Batch was excuted with ExecuteType.Batch, but annotation-base queries will be excuted with ExecuteType.SIMPLE, which is having differentition point.

 

1.1 Trouble Shooting and Solution

Implement MyBatisConfig with returning SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);

Let's show e.g

import org.apache.ibatis.session.ExecutorType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
@MapperScan("your.package.com.model.mapper")
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // Ensure that the mapper XML files are found
        factoryBean.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/*.xml")
        );
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.SIMPLE);
    }
}

 

@MapperScan Annotation could be matched with interface of mapper location. if non-correct path was assigned, mapper interface do not find real object.

 

setMapperLocation  method  will be matched with mybatis.mapper-location in application.yml.
So, We need to match mapper-location path like classpath:*mappers/*.xml

 

Since I use absolute path in application.yml, which is unusuall case, classpath solution could not be fitted.

In theory, we would use classpath with ExecutorType.SIMPLE.

 

 

[Concenturate to understand issue environment]

MyBatisPageReaderBuilder will return ExecutorType.Batch, and this part causes Spring Batch ExecutorType not to be matched issue.

 

    @Bean
    @StepScope
    public MyBatisCursorItemReader<______Dto__or__Entity> ___ItemReader(
            @Value("#{jobParameters[UUID]}") String uuid) throws Exception {
        Map<String, Object> parameterValues = new HashMap<>();
        parameterValues.put("uuid", uuid);

        MyBatisCursorItemReaderBuilder<YourDto> readerBuilder = new MyBatisCursorItemReaderBuilder<>();
        readerBuilder.sqlSessionFactory(sqlSessionFactory);
        readerBuilder.queryId("your.package.com.model.mapper.InterfaceMapper.select");
        readerBuilder.parameterValues(parameterValues);

        return readerBuilder.build();
    }

 

 

 

Quote

https://jessyt.tistory.com/5

 

org.springframework.dao.TransientDataAccessResourceException: Cannot change the ExecutorType when there is an existing transacti

Batch 프로젝트를 진행중에 아래와 같은 에러 발생하였습니다. Batch 삽질 삽질 삽질 에러 원인을 요약하자면 하나의 트랜잭션 안에서 다른 실행자를 사용할 수 없다라는 내용이다. 자세한 내용을

jessyt.tistory.com

https://bkjeon1614.github.io/blog/java-springbatch-7

 

Spring Batch 7편 - ItemReader - 아무거나 (github)

단, 본인 조회 프레임워크가 Querydsl, Jooq 라면 직접 구현해야할 수도 있다. 왜냐하면 왠만해선 JdbcItemReader 로 해결되지만, JPA 영속성 컨텍스트 지원이 안되서 HibernateItemReader 를 사용하여 Reader 구

bkjeon1614.github.io

 

 

'Java' 카테고리의 다른 글

Pattern and Matcher  (0) 2024.07.16
SecureRandom  (0) 2024.07.12
[MyBatis] Page with MyBatis  (0) 2024.07.01
[Spring Security] Public vs Private method to be applied Spring Security  (1) 2024.06.07
[method] java stream mapToObj  (0) 2024.05.07

+ Recent posts