我正在開發mysql主從複製。我使用彈簧數據jpa(彈簧引導)。將自定義註釋建議應用於彈簧數據jpa存儲庫
我需要的是所有寫入操作去主服務器和只讀操作平等分佈在多個只讀從站。
對於我需要:
使用特殊的JDBC驅動程序:com.mysql.jdbc.ReplicationDriver
設置複製:在網址:
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連接設置爲只讀,我創建了一個註釋和一個簡單的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註釋是在管理器或服務層中指定的。 以上幾段代碼對我來說工作得很好。這是一個微不足道的情況,在服務層,我只從slave db讀取數據並寫入master db。
說了我的實際需求是我應該能夠在存儲庫級別本身使用@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的世界裏,我試圖相當的ConnectionInterceptor幾個切入點正則表達式,但沒有一次成功。我很長一段時間以來一直在嘗試,但沒有成功。
如何實現問題任務。
您是否已將'@ EnableAspectJAutoProxy'添加到您的配置中? – px5x2
是的。我做到了。我提到「上面的代碼對我來說工作正常」。我沒有把所有的代碼放在這裏。我只放了一段重要的代碼片段。 –
嘿Bhupati,你是否能夠實現你的目標在你的存儲庫類中指定@Transactional Annotation?如果是,你做了什麼改變。你可以在這裏發佈解決方案來幫助其他人(比如我:P)。謝謝 –