이 영역을 누르면 첫 페이지로 이동
천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

페이지 맨 위로 올라가기

천천히 꾸준히 조용히

천천히 꾸준히 조용히.. i3months 블로그

[Spring Batch] 메타데이터 테이블과 시퀀스

  • 2024.11.05 13:47
  • 💡 솔루션
반응형

 

 

 

org.springframework.dao.DataAccessResourceFailureException: Could not obtain sequence value

 

 

스프링배치로 애플리케이션을 만들고 실행할 때 실행 관련 상태를 저장하는 메타데이터 테이블을 관리하게 된다.

 

시스템에서 SQL Server를 사용하는 경우 메타데이터 테이블에 값을 넣을 때 시퀀스 객체를 사용하는데.. 해당 DBMS에서 시퀀스 객체를 지원하지 않는 경우 위와 같은 오류가 발생한다.

 

 

소스코드를 까보면 DefaultDataFieldMaxValueIncrementerFactory 클래스를 확인할 수 있다.

 

public class DefaultDataFieldMaxValueIncrementerFactory implements DataFieldMaxValueIncrementerFactory {

	private final DataSource dataSource;

	private String incrementerColumnName = "ID";

	/**
	 * Public setter for the column name (defaults to "ID") in the incrementer. Only used
	 * by some platforms (Derby, HSQL, MySQL, SQL Server and Sybase), and should be fine
	 * for use with Spring Batch meta data as long as the default batch schema hasn't been
	 * changed.
	 * @param incrementerColumnName the primary key column name to set
	 */
	public void setIncrementerColumnName(String incrementerColumnName) {
		this.incrementerColumnName = incrementerColumnName;
	}

	public DefaultDataFieldMaxValueIncrementerFactory(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) {
		DatabaseType databaseType = DatabaseType.valueOf(incrementerType.toUpperCase());

		if (databaseType == DB2 || databaseType == DB2AS400) {
			return new Db2LuwMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == DB2ZOS) {
			return new Db2MainframeMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == DERBY) {
			return new DerbyMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
		}
		else if (databaseType == HSQL) {
			return new HsqlMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
		}
		else if (databaseType == H2) {
			return new H2SequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == HANA) {
			return new HanaSequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == MYSQL) {
			MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer(dataSource,
					incrementerName, incrementerColumnName);
			mySQLMaxValueIncrementer.setUseNewConnection(true);
			return mySQLMaxValueIncrementer;
		}
		else if (databaseType == MARIADB) {
			return new MariaDBSequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == ORACLE) {
			return new OracleSequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == POSTGRES) {
			return new PostgresSequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == SQLITE) {
			return new SqliteMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
		}
		else if (databaseType == SQLSERVER) {
			return new SqlServerSequenceMaxValueIncrementer(dataSource, incrementerName);
		}
		else if (databaseType == SYBASE) {
			return new SybaseMaxValueIncrementer(dataSource, incrementerName, incrementerColumnName);
		}
		throw new IllegalArgumentException("databaseType argument was not on the approved list");
	}

	@Override
	public boolean isSupportedIncrementerType(String incrementerType) {
		for (DatabaseType type : DatabaseType.values()) {
			if (type.name().equalsIgnoreCase(incrementerType)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public String[] getSupportedIncrementerTypes() {

		List<String> types = new ArrayList<>();

		for (DatabaseType type : DatabaseType.values()) {
			types.add(type.name());
		}

		return types.toArray(new String[types.size()]);
	}

}

 

 

getIncrementer 메서드를 확인해보면 시스템에서 사용하는 DBMS에 따라 다른 incrementer를 반환하고 있음을 확인할 수 있다.

 

 

public class SqlServerSequenceMaxValueIncrementer extends AbstractSequenceMaxValueIncrementer {

	/**
	 * Default constructor for bean property style usage.
	 * @see #setDataSource
	 * @see #setIncrementerName
	 */
	public SqlServerSequenceMaxValueIncrementer() {
	}

	/**
	 * Convenience constructor.
	 * @param dataSource the DataSource to use
	 * @param incrementerName the name of the sequence to use
	 */
	public SqlServerSequenceMaxValueIncrementer(DataSource dataSource, String incrementerName) {
		super(dataSource, incrementerName);
	}

	@Override
	protected String getSequenceQuery() {
		return "select next value for " + getIncrementerName();
	}

}

 

 

SQL Server를 사용하는 경우 SqlServerSequenceMaxValueIncrementer 클래스를 사용하는데.. 
해당 부분을 살펴보면 "SELECT NEXT VALUE FOR" 구문으로 DBMS의 시퀀스 객체를 사용하는 쿼리를 확인할 수 있다.

 

SQL Server 2012 버전부터 도입됐고, 스프링배치에서는 사용하는 DBMS만 확인할 뿐 DBMS의 버전을 따로 확인하지는 않는다.

SQL Server 2005 버전을 사용하는 경우 시퀀스를 지원하지 않아 메타데이터 테이블에 값을 넣을 때 오류가 발생한다. 

 

Linked Server 를 사용해서 다른 데이터베이스에 메타데이터 테이블을 구축하는 방법도 있지만..

시스템에서 해당 부분만 수정해서 시퀀스를 지원하지 않는 DBMS에서도 스프링 배치를 사용할 수 있도록 설정하자. 

 

 

public class CustomDefaultDataFieldMaxValueIncrementerFactory extends DefaultDataFieldMaxValueIncrementerFactory{
    private final DataSource dataSource;

    public CustomDefaultDataFieldMaxValueIncrementerFactory(DataSource dataSource) {
        super(dataSource);
        this.dataSource = dataSource;
    }
    @Override
    public DataFieldMaxValueIncrementer getIncrementer(String databaseType, String incrementerName) {
        CustomIncrementer customIncrementer = new CustomIncrementer(this.dataSource, incrementerName);
        return customIncrementer;
    }

}

 

 

우선 해당 클래스를 상속해 새로 구현한 Incrementer를 사용하도록 설정하자. 

 

public class CustomIncrementer extends AbstractColumnMaxValueIncrementer {

    public CustomIncrementer(DataSource dataSource, String incrementerName) {
        super(dataSource, incrementerName, "ID");
    }

    @Override
    protected long getNextKey() {
        long nextKey = 0;
        try (Connection conn = this.getDataSource().getConnection()) {
            PreparedStatement psSelect = conn.prepareStatement("SELECT ID FROM " + getIncrementerName() + " WITH (UPDLOCK, HOLDLOCK)");
            ResultSet rs = psSelect.executeQuery();
            if (rs.next()) {
                nextKey = rs.getLong(1);
            }
            rs.close();
            psSelect.close();

            PreparedStatement psUpdate = conn.prepareStatement("UPDATE " + getIncrementerName() + " SET ID = ?");
            psUpdate.setLong(1, nextKey + 1);
            psUpdate.executeUpdate();
            psUpdate.close();
        } catch (SQLException e) {
            throw new RuntimeException("Could not get next key for " + getIncrementerName(), e);
        }
        return nextKey + 1;
    }

}

 

 

시퀀스 대신 테이블을 시퀀스 처럼 사용하자.

 

 

 

 

DBMS에서 사용하는 시퀀스와 똑같은 이름을 가진 테이블을 만들고, 그 테이블에 column과 row를 하나만 만들어준다.

그 값 하나를 1씩 증가시키면서 사용해 시퀀스처럼 사용하자. (BATCH_JOB_SEQ)

 

시퀀스 객체를 직접 사용하는 것 보다 동시성, 무결성은 떨어질 수 있지만..

이렇게 설정해주면 시퀀스 객체를 지원하지 않는 버전에서도 스프링 배치의 메타데이터 테이블을 사용할 수 있다. 

 

 

반응형
저작자표시 (새창열림)

'💡 솔루션' 카테고리의 다른 글

[Tomcat] 네트워크 드라이브 권한 관련 오류  (0) 2024.11.09
[PDF.js] PDF.js 완벽 가이드  (3) 2024.11.07
[Spring Security] 인증 실패 오류 다루기  (0) 2024.06.20
[SQL Server] 지원하지 않는 TLS 버전 설정  (1) 2024.06.05
대용량 파일 업로드 처리 (30GB)  (1) 2023.12.03

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [Tomcat] 네트워크 드라이브 권한 관련 오류

    [Tomcat] 네트워크 드라이브 권한 관련 오류

    2024.11.09
  • [PDF.js] PDF.js 완벽 가이드

    [PDF.js] PDF.js 완벽 가이드

    2024.11.07
  • [Spring Security] 인증 실패 오류 다루기

    [Spring Security] 인증 실패 오류 다루기

    2024.06.20
  • [SQL Server] 지원하지 않는 TLS 버전 설정

    [SQL Server] 지원하지 않는 TLS 버전 설정

    2024.06.05
다른 글 더 둘러보기

정보

천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

  • 천천히 꾸준히 조용히의 첫 페이지로 이동

검색

방문자

  • 전체 방문자
  • 오늘
  • 어제

카테고리

  • 분류 전체보기 (675) N
    • Algorithm (205)
      • Data Structure (5)
      • Theory && Tip (33)
      • Baekjoon (166)
      • ALGOSPOT (1)
    • Spring (123)
      • Spring (28)
      • Spring Web MVC (20)
      • Spring Database (14)
      • Spring Boot (6)
      • Spring 3.1 (11)
      • Spring Batch (6)
      • Spring Security (16)
      • JPA (12)
      • Spring Data JPA (5)
      • QueryDSL (4)
      • eGovFramework (1)
    • Programming Language (74)
      • C (25)
      • C++ (12)
      • Java (19)
      • JavaScript (15)
      • Python (1)
      • PHP (2)
    • Computer Science (142)
      • Machine Learning (38)
      • Operating System (18)
      • Computer Network (28)
      • System Programming (22)
      • Universial Programming Lang.. (8)
      • Computer Architecture (4)
      • Compiler Design (11)
      • Computer Security (13)
    • Database (21)
      • Database (7)
      • MySQL (3)
      • Oracle (3)
      • Redis (5)
      • Elasticsearch (3)
    • DevOps (20)
      • Docker && Kubernetes (8)
      • Jenkins (4)
      • Amazon Web Service (8)
    • Mobile (28)
      • Android (21)
      • Flutter (7)
    • 💡 솔루션 (17)
    • 👥 모각코 (8)
    • 💬 기록 (7) N
    • 📚 공부 (5)
    • -------------- (25)

최근 글

나의 외부 링크

메뉴

  • 홈
반응형

정보

i3months의 천천히 꾸준히 조용히

천천히 꾸준히 조용히

i3months

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. Copyright © i3months.

티스토리툴바