Применение пользовательских советов по аннотации к хранилищу данных JPA Spring

Я работаю над репликацией master-slave MySQL. Я использую данные весны jpa (весенняя загрузка).

Все, что мне нужно, - это все операции записи для перехода на главный сервер и операции только для чтения, которые будут равномерно распределены между несколькими ведомыми устройствами только для чтения.

Для этого мне нужно:

Используйте специальный драйвер JDBC: com.mysql.jdbc.ReplicationDriver

Установить репликацию: в URL:

spring:
    datasource:
        driverClassName: com.mysql.jdbc.ReplicationDriver
        url: jdbc:mysql:replication://127.0.0.1:3306,127.0.0.1:3307/MyForum?user=root&password=password&autoReconnect=true
        test-on-borrow: true
        validation-query: SELECT 1
    database: MYSQL

Авто фиксация должна быть отключена. (*) Соединение должно быть установлено только для чтения.

Чтобы JDBC Connection был доступен только для чтения, я создал аннотацию и простой перехватчик AOP.

аннотирование

package com.xyz.forum.replication;

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

/**
 * Created by Bhupati Patel on 02/11/15.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ReadOnlyConnection {
}

истребитель-перехватчик

package com.xyz.forum.replication;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;

/**
 * Created by Bhupati Patel on 02/11/15.
 */

@Aspect
@Component
public class ConnectionInterceptor {

    private Logger logger;

    public ConnectionInterceptor() {
        logger = LoggerFactory.getLogger(getClass());
        logger.info("ConnectionInterceptor Started");
    }

    @Autowired
    private EntityManager entityManager;

    @Pointcut("@annotation(com.xyz.forum.replication.ReadOnlyConnection)")
    public void inReadOnlyConnection(){}


    @Around("inReadOnlyConnection()")
    public Object proceed(ProceedingJoinPoint pjp) throws Throwable {
        Session session = entityManager.unwrap(Session.class);
        ConnectionReadOnly readOnlyWork = new ConnectionReadOnly();

        try{
            session.doWork(readOnlyWork);
            return pjp.proceed();
        } finally {
            readOnlyWork.switchBack();
        }
    }

}

Следующее - это мое весеннее хранилище данных

package com.xyz.forum.repositories;

import com.xyz.forum.entity.Topic;
import org.springframework.data.repository.Repository;

import java.util.List;

/**
 * Created by Bhupati Patel on 16/04/15.
 */
public interface TopicRepository extends Repository<Topic,Integer>{
    Topic save(Topic topic);
    Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
    List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);

}

Ниже приведен мой класс Менеджер (Сервис).

package com.xyz.forum.manager;

import com.xyz.forum.domain.entry.impl.TopicEntry;

import com.xyz.forum.domain.exception.impl.AuthException;

import com.xyz.forum.domain.exception.impl.NotFoundException;
import com.xyz.forum.entity.Topic;
import com.xyz.forum.replication.ReadOnlyConnection;
import com.xyz.forum.repositories.TopicRepository;
import com.xyz.forum.utils.converter.TopicConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * Created by Bhupati Patel on 16/04/15.
 */
@Repository
public class TopicManager {
    @Autowired
    TopicRepository topicRepository;

    @Transactional
    public TopicEntry save(TopicEntry topicEntry) {
        Topic topic = TopicConverter.fromEntryToEntity(topicEntry);
        return TopicConverter.fromEntityToEntry(topicRepository.save(topic));
    }

    @ReadOnlyConnection
    public TopicEntry get(Integer id) {
        Topic topicFromDb = topicRepository.findByTopicIdAndIsDeletedFalse(id);
        if(topicFromDb == null) {
            throw new NotFoundException("Invalid Id", "Topic Id [" + id + "] doesn't exist ");
        }
        return TopicConverter.fromEntityToEntry(topicFromDb);
    }
}

В приведенном выше коде аннотация @ReadOnlyConnection указана на уровне менеджера или сервиса.Выше куски кода прекрасно работает для меня. Это тривиальный случай, когда на уровне сервиса я только читаю из ведомой базы данных и записываю в главную базу данных.

Сказав, что мое фактическое требование заключается в том, что я должен иметь возможность использовать @ReadOnlyConnection на самом уровне хранилища, потому что у меня довольно много бизнес-логики, в которой я выполняю операции чтения / записи в других классах уровня обслуживания. Поэтому я не могу поместить @ReadOnlyConnection в сервисном слое.

Я должен быть в состоянии использовать что-то вроде этого

public interface TopicRepository extends Repository<Topic,Integer>{
    Topic save(Topic topic);
    @ReadOnlyConnection
    Topic findByTopicIdAndIsDeletedFalse(Integer topicId);
    @ReadOnlyConnection
    List<Topic> findByIsDeletedOrderByTopicOrderAsc(Boolean isDelete);

}

Как в аннотации Spring @Transactional или @Modifying или @Query. Ниже приведен пример того, что я имею в виду.

    public interface AnswerRepository extends Repository<Answer,Integer> {
    @Transactional
    Answer save(Answer answer);

    @Transactional
    @Modifying
    @Query("update Answer ans set ans.isDeleted = 1, ans.deletedBy = :deletedBy, ans.deletedOn = :deletedOn " +
            "where ans.questionId = :questionId and ans.isDeleted = 0")
    void softDeleteBulkAnswers(@Param("deletedBy") String deletedBy, @Param("deletedOn") Date deletedOn,
                               @Param("questionId") Integer questionId);
}

Я новичок в aspectj и aop мире, я пробовал довольно много регулярных выражений pointcut в ConnectionInterceptor, но ни один из них не работал. Я пытался это с давних времен, но пока не повезло.

Как достичь поставленной задачи.

Ответы на вопрос(1)

Ваш ответ на вопрос