Java for Web 학습 노트(일일이):Entity 맵 만 들 기(4)동적 표 만 들 기

이 불확실 한 표 도 우리 의 war 가 만들어 야 한다 면 어떻게 실현 할 것 인가?create table 의 네 이 티 브 SQL,enity Manager 는 실행 할 수 없습니다.스크롤 백 할 수 있 는 업무 가 아니 기 때 문 입 니 다.이런 상황 에서 우 리 는 필요 하 다.
포획 표 에 존재 하지 않 는 이상
  • 원본 Connection 에서 표를 만 듭 니 다

  • Connection 가 져 오기 Entity Manage 에서 Connection 을 가 져 올 수 있 는 지 는 JPA 의 구체 적 인 실현 에 의존 합 니 다.Eclipse 는 지원 하지만 Hibernate 는 지원 하지 않 습 니 다.
    //    unwrap   ,  Hibernate   
    Connection conn = entityManager.unwrap(Connection.class);
    //        Hibernate   api,    unwrap()  Hibernate session
    Session session = entityManager.unwrap(Session.class);

    이 길 은 통 하지 않 습 니 다.DataSource 에서 가 져 와 야 합 니 다.
    표를 만 들 때 이상 을 던 집 니 다.
    public class WantCreateTableException extends RuntimeException{
    
    	private static final long serialVersionUID = 1L;
    
    	public WantCreateTableException(String message) {
    		super(message);
    	}
    }

    창고 코드 수정
    @Repository
    @Validated
    public class EventRepository {
    	private static final Logger log = LogManager.getLogger();
    	private static final Gson gson = new Gson();
    	@PersistenceContext private EntityManager entityManager;
    	@Inject private DataSource dataSource;
    	
    	private static final String CREATE_TABLE_SQLFORMAT = "CREATE TABLE IF NOT EXISTS `%s`("
    			+ "`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,"
    			+ "`data` text,"
    			+  "PRIMARY KEY (`id`)"
        		+ ")ENGINE=InnoDB DEFAULT CHARSET=utf8";
    	public void createTable(String tableName){
    		String sql = String.format(CREATE_TABLE_SQLFORMAT, tableName);
    		try(Connection conn = dataSource.getConnection();
    				PreparedStatement ps = conn.prepareStatement(sql);){
    			ps.execute();
    		}catch(SQLException e){
    			log.error("(SQL ERROR) Try to create table {} error : {}", tableName, e.toString());
    		}	
    	}	
    
    	private String getTableName(){
    		... ...
    	}
    	
    	public EventData findOne(Long id) {
    		log.traceEntry();
    		String tableName = getTableName();
    		try{
    			String sql = String.format("SELECT * FROM `%s` WHERE `id`=?", tableName,id);		
    			return  EventData.build((EventEntity) entityManager.createNativeQuery(sql,EventEntity.class)
    					.setParameter(1, id).getSingleResult());
    		}catch(Exception e){
    			return null;
    		}
    	}
    
    	public void save(EventData event) {
    		try{
    			if(event.getId() == null || event.getId() == 0)
    				this.insert(event);
    			else
    				this.update(event);		
    		}catch(Exception e){
    			if(isTableNotExist(e, getTableName()))
    				throw new WantCreateTableException(getTableName());
    			else
    				throw e;
    		}
    	}	
    
    	private boolean update(EventData data) {
                   ... ...	
    	}
    	
    	private void insert(EventData data) {
    		... ...
    	}
    
    	private boolean isTableNotExist(Exception e,String tableName){
    		if(e.getCause() == null || e.getCause().getCause() == null)
    			return false;
    		String message = e.getCause().getCause().getMessage();
    		return StringUtils.contains(message, tableName.concat("' doesn't exist"));
    	}
    	
    }

    사용 예
    @Service
    public class TestService {
    	@Transactional
    	public void test2(){
    		EventData event = new EventData();
    		event.addData("Hello,world!");
    		this.eventRepository.save(event);
    	}
    }

    다음은 테스트 예 일 뿐 입 니 다.저 희 는 contrller 에서 창 고 를 호출 했 습 니 다.이렇게 하면 실제 적 으로 좋 지 않 습 니 다.contrller 는 Service 만 호출 해 야 합 니 다.저 희 는 중간 에 업무 논리 서 비 스 를 가지 고 TestService 를 호출 하여 관련 처 리 를 완성 할 수 있 습 니 다.@Transactional 은 Runtime Exception 을 만 날 때 사 무 를 종료 합 니 다.사 무 는 proxy 방식 을 사용 합 니 다.즉,이러한 내부 호출 은 역할 을 하지 않 습 니 다.
    @Controller
    public class TestController {
    	@Inject TestPageService testService;
    	@Inject EventRepository eventRepository;
    	private void mytest(){
    		try{
    			testService.test2();
    		}catch(WantCreateTableException e){
    			this.eventRepository.createTable(e.getMessage());
    			testService.test2();
    		}
    	}
    }

    나 는 아래 와 같이 쓰 려 고 시 도 했 지만 실패했다.
    //      ,     Transaction was marked for rollback only; cannot commit;
    //     WantCreateTableException         ,       ,   hibernate     rollback:
    //  : Servlet.service() for servlet [springWebDispatcher] in context with path [/chapter22] threw exception 
    // [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: 
    // Transaction was marked for rollback only; cannot commit; nested exception is org.hibernate.TransactionException: 
    // Transaction was marked for rollback only; cannot commit] with root cause
    @Transactional(noRollbackFor = WantCreateTableException.class) 
    public void test2(){
    	EventData event = new EventData();
    	event.addData("Hello,world!");		
    	try{
    		this.eventRepository.save(event);
    	}catch(WantCreateTableException e){
    		this.eventRepository.createTable(e.getMessage());
    		this.eventRepository.save(event);
    	}
    }
    관련 링크:나의 Professional Java for Web Applications 관련 글

    좋은 웹페이지 즐겨찾기