Play 1.x 프레임워크 학습 7: 다중 데이터베이스 전환 및 소스 수정(Databases Switch And Modify Source Code)

단일 데이터 원본 (단일 ip) 아래의 다중 라이브러리는use xxdb 명령을 사용하여 전환할 수 있지만, 여러 데이터 원본의 경우use 명령은 안 됩니다.플레이 프레임워크에서 다중 데이터 원본 다중 라이브러리 전환을 제공합니다.본고는 완전한 예를 제공하지 않고 부분적인 코드만 제공하며 중점은 뒤의 원본 코드를 수정하는 것이다.다중 IP 다중 라이브러리 전환이 필요하다면, 모든 라이브러리의 정보를 저장하는 메인 라이브러리가 있어야 합니다.클라우드 응용 프로그램과 같이 모든 임차인의 데이터 원본과 라이브러리 이름을 저장해야 한다. 왜냐하면 여러 개의 라이브러리가 하나의 서버를 공용하고 여러 개의 서버가 집단 클라우드 응용을 구성하기 때문이다.우선 데이터 원본을 설정해야 합니다. 메인 라이브러리의db와 라이브러리db_01: Conf/application.conf
jpa.dialect=org.hibernate.dialect.MySQLDialect
db.url=jdbc:mysql://basedbip:3306/pop?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
db.driver=com.mysql.jdbc.Driver
db.user=root
db.pass=root
db=pop

db_01.url=jdbc:mysql://anotherdbip:3306/pop?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
db_01.driver=com.mysql.jdbc.Driver
db_01.user=root
db_01.pass=root
db_01=pop01

라이브러리를 자르는 방법은 다음과 같습니다. Util/DB.java
package util;

import play.db.jpa.JPA;

public class DB {
    public static void changeDB(String dbconfigname, String dbname){
        try {
            JPA.setCurrentConfigName(dbconfigname);
            if (JPA.em().getTransaction().isActive() == false) {
                JPA.em().getTransaction().begin();
            }
            JPA.em().createNativeQuery("use "+dbname).executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

상술한 설정을 완성하면 라이브러리를 자유롭게 절단할 수 있지만, 실제 실행 중이라면 중대한 문제가 발생할 수 있습니다.컨트롤러 층에서 데이터 봉인과 관련된 것입니다. 예를 들어 사용자 수정:
public static void update(@Required @Valid User user){
    user.save();
    renderText(user.id);
}

user 대상 파라미터를 봉인할 때play1.x 프레임워크의 자동 조회 데이터베이스입니다. 이 처리는 컨트롤러 방법 이전이기 때문에 라이브러리를 절단할 수 없습니다!메인 라이브러리와 라이브러리 테이블과 데이터가 다르기 때문에 오류가 발생합니다!
play.exceptions.UnexpectedException: Unexpected Error
    at play.data.validation.ValidationPlugin.beforeActionInvocation(ValidationPlugin.java:59)
    at play.plugins.PluginCollection.beforeActionInvocation(PluginCollection.java:518)
    at play.mvc.ActionInvoker.invoke(ActionInvoker.java:131)
    at Invocation.HTTP Request(Play!)
Caused by: play.exceptions.UnexpectedException: Unexpected Error
    at play.db.jpa.JPAPlugin.bind(JPAPlugin.java:67)
    at play.plugins.PluginCollection.bind(PluginCollection.java:468)
    at play.data.binding.Binder.bind(Binder.java:309)
    at play.mvc.ActionInvoker.getActionMethodArgs(ActionInvoker.java:621)
    at play.data.validation.ValidationPlugin$Validator.validateAction(ValidationPlugin.java:95)
    at play.data.validation.ValidationPlugin.beforeActionInvocation(ValidationPlugin.java:51)
    ... 3 more
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1214)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)
    at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:307)
    at play.db.jpa.JPAPlugin.bind(JPAPlugin.java:62)
    ... 8 more
Caused by: org.hibernate.exception.SQLGrammarException: could not execute query
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.loader.Loader.doList(Loader.java:2536)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
    at org.hibernate.loader.Loader.list(Loader.java:2271)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:452)
    at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:363)
    at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1268)
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:274)
    ... 9 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table ´pop.user´ doesn´t exist
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:407)
    at com.mysql.jdbc.Util.getInstance(Util.java:382)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1052)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3593)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3525)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1986)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2140)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2626)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2111)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2273)
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1953)
    at org.hibernate.loader.Loader.doQuery(Loader.java:802)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    at org.hibernate.loader.Loader.doList(Loader.java:2533)
    ... 17 more

오류 창고의 마지막 메시지를 보았습니다. 시계가 존재하지 않습니다!부모 컨트롤러 컨트롤러에서 @before 탭을 이용하여 방법이 호출되기 전에 차단 설정을 통일하고 싶을 수도 있습니다.하지만 결과는 너를 실망시킬 거야!잘못은 여전할 거야!이때 우리는 오류 정보에 근거하여play 프레임워크의 원본 코드를 깊이 있게 해야 한다.caused by에서 JPA Plugin이 보입니다.java, 그런데 이미 JPA 조회 데이터베이스에 썼어요.두 번째 caused by 에서 JPA Plugin.java: 62곳에서 jpaplugin을 볼 수 있는 bind 방법은 다음과 같습니다.
@Override
@SuppressWarnings("unchecked")
public Object bind(String name, Class clazz, java.lang.reflect.Type type, Annotation[] annotations, Map params) {
    // TODO need to be more generic in order to work with JPASupport
    if (JPABase.class.isAssignableFrom(clazz)) {
        String keyName = Model.Manager.factoryFor(clazz).keyName();
        String idKey = name + "." + keyName;
        if (params.containsKey(idKey) && params.get(idKey).length > 0 && params.get(idKey)[0] != null && params.get(idKey)[0].trim().length() > 0) {
            String id = params.get(idKey)[0];
            try {
                Query query = JPA.em().createQuery("from " + clazz.getName() + " o where o." + keyName + " = ?");
                query.setParameter(1, play.data.binding.Binder.directBind(name, annotations, id + "", Model.Manager.factoryFor(clazz).keyType()));
                Object o = query.getSingleResult();
                return GenericModel.edit(o, name, params, annotations);
            } catch (NoResultException e) {
                // ok
            } catch (Exception e) {
                throw new UnexpectedException(e);
            }
        }
        return GenericModel.create(clazz, name, params, annotations);
    }
    return super.bind(name, clazz, type, annotations, params);
}

일목요연하다
Query query = JPA.em().createQuery("from " + clazz.getName() + " o where o." + keyName + " = ?");

메인 키에 따라 데이터 테이블에 데이터를 조회하지만 이때 해당하는 라이브러리를 자르지 않았기 때문에 조회표가 없습니다!(메인 라이브러리에 해당하는 표가 있어도 해당하는 데이터가 없다.)왜 창고를 자르지 않았다고 해요?@before로 처리했잖아요.ActionInvoker에 대한 잘못된 정보를 계속 찾습니다.java의 invoke 방법, 코드가 너무 많아서 잘못된 알림만 붙인 131행 부근
ControllerInstrumentation.stopActionCall();
        Play.pluginCollection.beforeActionInvocation(actionMethod);

        // Monitoring
         monitor = MonitorFactory.start(request.action + "()");

        // 3. Invoke the action
        try {
            // @Before
            handleBefores(request);

            // Action

            Result actionResult = null;
            String cacheKey = null;

여기 있습니다.
Play.pluginCollection.beforeActionInvocation(actionMethod);

play 프레임워크는 일련의 플러그인 호출 전 준비 작업을 처리합니다. redisplugin/jpaplugin/validationplugin/wsplugin 등입니다. 그 중에서 validationplugin은 모든 검증 주석이 있는 실체 클래스를 처리하는 동시에 @id 주석의 메인 키를 눌러서 @before를 처리하는 방법도 포함합니다.
handleBefores(request);

그러니까 befores를 써도 소용없어!이 실행 가능한 방안은play 원본 코드를 수정하고befores 주석을 본떠서 처리하는 것입니다.mvc에서 BeforeValidation 메모를 추가하여 검증하기 전에 라이브러리를 잘라냅니다!
play.mvc.BeforeValidation.java
package play.mvc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Mark this method as @BeforeValidation interceptor
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeValidation { 
    
    /**
     * Does not intercept these actions
     */
    String[] unless() default {};
    String[] only() default {};

    /**
     * Interceptor priority (0 is high priority)
     */
    int priority() default 0; 
}

동시에 ActionInvoker에 메서드 handleBeforeValidations 재생을 추가합니다.mvc.ActionInvoker.java
private static void handleBefores(Http.Request request) throws Exception {
        List befores = Java.findAllAnnotatedMethods(Controller.getControllerClass(), Before.class);
        Collections.sort(befores, new Comparator() {

            public int compare(Method m1, Method m2) {
                Before before1 = m1.getAnnotation(Before.class);
                Before before2 = m2.getAnnotation(Before.class);
                return before1.priority() - before2.priority();
            }
        });
        ControllerInstrumentation.stopActionCall();
        for (Method before : befores) {
            String[] unless = before.getAnnotation(Before.class).unless();
            String[] only = before.getAnnotation(Before.class).only();
            boolean skip = false;
            for (String un : only) {
                if (!un.contains(".")) {
                    un = before.getDeclaringClass().getName().substring(12).replace("$", "") + "." + un;
                }
                if (un.equals(request.action)) {
                    skip = false;
                    break;
                } else {
                    skip = true;
                }
            }
            for (String un : unless) {
                if (!un.contains(".")) {
                    un = before.getDeclaringClass().getName().substring(12).replace("$", "") + "." + un;
                }
                if (un.equals(request.action)) {
                    skip = true;
                    break;
                }
            }
            if (!skip) {
                before.setAccessible(true);
                inferResult(invokeControllerMethod(before));
            }
        }
    }

HandleBeforeValidations 처리 방법을 Play에 추가합니다.pluginCollection.before Action Invocation 앞.
ControllerInstrumentation.stopActionCall();
    
    //@BeforeValidation
    handleBeforeValidations(request);
    
    Play.pluginCollection.beforeActionInvocation(actionMethod);

    // Monitoring
    monitor = MonitorFactory.start(request.action + "()");

    // 3. Invoke the action
    try {
        // @Before
        handleBefores(request);

        // Action

        Result actionResult = null;
        String cacheKey = null;

이렇게 하면 BeforeValidation 주석을 통해 응용 프로그램에 해당하는 라이브러리 논리를 추가할 수 있다.Jpa 봉인 대상 매개 변수의 오류 문제를 해결할 수 있습니다.또한 라이브러리 커팅 근거는 상응하는 데이터 원본 등 데이터를 하나의 대상에 저장하고sessionid로 키를 만들어redis에 저장할 수 있으며 요청할 때마다 라이브러리 커팅을 하고 로그인 여부를 검사할 수 있다.

좋은 웹페이지 즐겨찾기