Anwenden eines benutzerdefinierten Kommentars auf das jpa-Repository für Frühlingsdaten

Ich arbeite an einer MySQL-Master-Slave-Replikation. Ich verwende Spring Data JPA (Spring Boot).

Was ich brauchte, ist, dass alle Schreibvorgänge zum Master-Server gehen und schreibgeschützte Vorgänge gleichmäßig auf mehrere schreibgeschützte Slaves verteilt werden.

afür muss ich:

Verwenden Sie einen speziellen JDBC-Treiber: com.mysql.jdbc.ReplicationDriver

Replikation einstellen: in der 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

Auto Commit muss ausgeschaltet sein. (*) Die Verbindung muss schreibgeschützt sein.

Um sicherzustellen, dass die JDBC-Verbindung schreibgeschützt ist, habe ich eine Anmerkung und einen einfachen AOP-Interceptor erstellt.

Anmerkun

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 {
}

Interceptor

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();
        }
    }

}

Folgend ist mein Frühjahrsdaten-Repository

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);

}

Folgend ist meine Manager (Service) -Klasse.

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);
    }
}

Im obigen Code wird die Anmerkung @ReadOnlyConnection in der Manager- oder Service-Schicht angegeben. Über Code funktioniert gut für mich. Es ist ein trivialer Fall, in dem ich in der Service-Schicht nur von der Slave-Datenbank lese und in die Master-Datenbank schreibe.

achdem ich gesagt habe, dass meine eigentliche Anforderung ist, dass ich in der Lage sein sollte, @ReadOnlyConnection auf Repository-Ebene selbst zu verwenden, da ich über eine ganze Reihe von Geschäftslogiken verfüge, bei denen ich sowohl Lese- als auch Schreibvorgänge in anderen Service-Layer-Klassen durchführe. ReadOnlyConnection in der Serviceebene.

Ich sollte in der Lage sein, so etwas zu verwenden

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

}

Transactional- oder @Modifying- oder @Query-Annotation von @Like Spring. Das Folgende ist ein Beispiel für das, was ich beziehe.

    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);
}

Ich bin Neuling in Aspektj und aop Welt, ich habe einige pointcut reguläre Ausdrücke im ConnectionInterceptor ausprobiert, aber keiner von ihnen hat funktioniert. Ich habe es seit langer Zeit versucht, aber noch kein Glück.

Wie erreiche ich die gestellte Aufgabe?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage