MyBatis 차단기 의 원리 와 사용
마 이 배 티 스 차단 기 는 다 오 가 DB 중간 까지 추가 처리 하 는 역할 을 한다.대부분의 경우 my batis 의 xml 설정 sql 을 통 해 원 하 는 DB 작업 효 과 를 얻 을 수 있 지만 유사 하거나 같은 조회 조건 이나 조회 요구 가 존재 합 니 다.이런 것들 은 차단기 의 실현 을 통 해 개발 효율 을 향상 시 킬 수 있 습 니 다.예 를 들 어 페이지,삽입 과 업데이트 시간/사람,데이터 권한,SQL 모니터링 로그 등 입 니 다.
Mybatis 는 네 가지 대상 이 Executor,StatementHandler,PameterHandler 와 ResultSetHandler 를 차단 하 는 것 을 지원 합 니 다
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
}
public interface StatementHandler {
Statement prepare(Connection var1, Integer var2) throws SQLException;
void parameterize(Statement var1) throws SQLException;
void batch(Statement var1) throws SQLException;
int update(Statement var1) throws SQLException;
<E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
<E> Cursor<E> queryCursor(Statement var1) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement var1) throws SQLException;
}
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> var1);
}
차단 실행 순 서 는 Executor->StatementHandler->ParameterHandler->ResultHandlerMyBatis 가 제공 하 는 차단기 인터페이스:
public interface Interceptor {
Object intercept(Invocation var1) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {}
}
Object intercept 방법 은 차단기 의 실현 에 사 용 됩 니 다.Object plugin 방법 은 실행 차단기 의 유형 을 판단 하 는 데 사 용 됩 니 다.
void setProperties 방법 은 설정 항목 의 속성 을 가 져 오 는 데 사 용 됩 니 다.
4.567917.차단 대상 과 차단기 인터페이스의 결합,사용자 정의 차단기 류 는 차단기 인 터 페 이 스 를 실현 하고@Intercepts 와 파라미터@Signature 를 통 해 차단 대상 을 설명 합 니 다 @Signature 매개 변수 type 은 차단 대상 이 고 method 는 차단 하 는 방법 입 니 다.즉,위의 네 가지 대응 하 는 방법 입 니 다.args 는 차단 방법 에 대응 하 는 매개 변수 입 니 다.
@Intercepts 는 여러 개의@Signature,즉 하나의 차단기 실현 클래스 가 여러 개의 대상 과 방법 을 동시에 차단 할 수 있 습 니 다.예 를 들 어 다음 과 같 습 니 다.
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class SelectPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof Executor) {
System.out.println("SelectPlugin");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {}
}
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class StatementPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof StatementHandler) {
System.out.println("StatementPlugin");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {}
}
@Intercepts({@Signature(type = ParameterHandler.class,method = "setParameters",args = {PreparedStatement.class})})
public class ParameterPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof ParameterHandler) {
System.out.println("ParameterPlugin");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof ParameterHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {}
}
@Intercepts({@Signature(type = ResultHandler.class,method = "handleResult",args = {ResultContext.class})})
public class ResultPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof ResultHandler) {
System.out.println("ResultPlugin");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof ResultHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {}
}
2.차단기 등록 의 세 가지 방식앞에서 Mybatis 의 차단 대상 과 인터페이스의 실현 방식 을 소 개 했 는데 프로젝트 에 차단 기 를 어떻게 등록 합 니까?본문 에서 세 가지 등록 방식 을 제시 하 다.
1.XML 등록
xml 등록 은 가장 기본 적 인 방식 으로 Mybatis 설정 파일 에서 plugins 요 소 를 통 해 등록 합 니 다.하나의 plugin 은 하나의 차단기 에 대응 합 니 다.plugin 요 소 는 property 서브 요 소 를 지정 할 수 있 습 니 다.정의 차단기 에 등록 할 때 해당 하 는 모든 property 를 Interceptor 의 setProperties 방법 으로 차단기 에 주입 합 니 다.따라서 차단기 등록 xml 방식 은 다음 과 같 습 니 다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- ...... -->
<plugins>
<plugin interceptor="com.tiantian.mybatis.interceptor.MyInterceptor">
<property name="prop1" value="prop1"/>
<property name="prop2" value="prop2"/>
</plugin>
</plugins>
<!-- ...... -->
</configuration>
2.클래스 등록 설정설정 클래스 등록 이란 Mybatis 의 설정 클래스 를 통 해 등록 차단 기 를 설명 하고,설정 클래스 등록 도 Properties 클래스 를 통 해 Interceptor 의 setProperties 방법 에 파 라미 터 를 주입 할 수 있 습 니 다.구체 적 인 참 고 는 다음 과 같다.
@Configuration
public class MyBatisConfig {
@Bean
public String MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) {
UpdatePlugin executorInterceptor = new UpdatePlugin();
Properties properties = new Properties();
properties.setProperty("prop1", "value1");
//
executorInterceptor.setProperties(properties);
sqlSessionFactory.getConfiguration().addInterceptor(executorInterceptor);
sqlSessionFactory.getConfiguration().addInterceptor(new StatementPlugin());
sqlSessionFactory.getConfiguration().addInterceptor(new ResultPlugin());
sqlSessionFactory.getConfiguration().addInterceptor(new ParameterPlugin());
// sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());
return "interceptor";
}
// sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());
@Bean
public SelectPlugin SelectInterceptor() {
SelectPlugin interceptor = new SelectPlugin();
Properties properties = new Properties();
// properties.setProperty
interceptor.setProperties(properties);
return interceptor;
}
}
3.주해 방식@Component 주해 방식 을 통 해 가장 간단 한 방법 으로 사용자 정의 파 라 메 터 를 전달 하지 않 아 도 사용 할 수 있 고 빠 르 고 편리 합 니 다.
@Component
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class SelectPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof Executor) {
System.out.println("SelectPlugin");
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
3.ParameterHandler 매개 변수 재 작성-수정 시간 과 수정 자 를 통일 적 으로 삽입 합 니 다.구체 적 인 차단기 실현 에 대해 설명 하 다.일상적인 인 코딩 수요 에서 수정 할 때 수 정 된 시간 과 인원 을 삽입 해 야 합 니 다.xml 방식 으로 쓰 려 면 매우 번 거 롭 고 차단 기 를 통 해 전체적인 삽입 수정 시간 과 인원 을 신속하게 실현 할 수 있 습 니 다.코드 먼저 보기:
@Component
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
})
public class MyBatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//
if (invocation.getTarget() instanceof ParameterHandler) {
System.out.println("ParameterHandler");
//
autoAddOperatorInfo(invocation);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
*
*
* @param invocation
* @throws Throwable
*/
private void autoAddOperatorInfo(Invocation invocation) throws Throwable {
System.out.println("autoInsertCreatorInfo");
// ParameterHandler
ParameterHandler ph = (ParameterHandler) invocation.getTarget();
// MetaObject ParameterHandler
MetaObject metaObject = MetaObject.forObject(ph,
SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
new DefaultReflectorFactory());
// MetaObject MappedStatement
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement");
// sql INSERT UPDATE ,
if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT ||
mappedStatement.getSqlCommandType() == SqlCommandType.UPDATE) {
//
Object obj = ph.getParameterObject();
if (null != obj) {
//
Field[] fields = obj.getClass().getDeclaredFields();
//
for (Field f : fields) {
// sql INSERT, createdAt
if ("createdAt".equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {
//
f.setAccessible(true);
// createdAt , createdAt
if (null == f.get(obj)) {
// createdAt
f.set(obj, LocalDateTime.now());
}
}
// sql INSERT, createdBy
if ("createdBy".equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {
//
f.setAccessible(true);
// createdBy , createdBy
if (null == f.get(obj)) {
// createdBy
f.set(obj, 0);
}
}
// sql INSERT UPDATE updatedAt
if ("updatedAt".equals(f.getName())) {
f.setAccessible(true);
if (null == f.get(obj)) {
f.set(obj, LocalDateTime.now());
}
}
// sql INSERT UPDATE updatedBy
if ("updatedBy".equals(f.getName())) {
f.setAccessible(true);
if (null == f.get(obj)) {
f.set(obj, 0);
}
}
}
// ParameterHandler parameterObject
Field parameterObject = ph.getClass().getDeclaredField("parameterObject");
// parameterObject
parameterObject.setAccessible(true);
// ParameterHandler parameterObject
parameterObject.set(ph, obj);
}
}
}
}
차단기 의 인 터 페 이 스 는 전문 을 참고 하여 autoAddOperator Info 방법 과 관련 된 종 류 를 소개 합 니 다.1.ParameterHandler
인터페이스 원본:
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement var1) throws SQLException;
}
두 가지 방법 을 제공 합 니 다.getParameterObject 는 매개 변 수 를 가 져 오 는 대상 입 니 다.null 이 존재 할 수 있 으 므 로 null 지침 에 주의해 야 합 니 다.
setParameters 는 sql 구문 에 설 정 된 자바 대상 과 jdbc 형식 에 대응 하 는 관 계 를 어떻게 설정 하 는 지 제어 합 니 다.예 를 들 어\#{id,jdbc Type=INTEGER},id 기본 형식 은 자바 Type=class 자바.lang.Integer 입 니 다.
이 인 터 페 이 스 는 기본 구현 클래스 가 있 습 니 다.원본 코드 는 다음 과 같 습 니 다.
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
public Object getParameterObject() {
return this.parameterObject;
}
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
if (parameterMappings != null) {
for(int i = 0; i < parameterMappings.size(); ++i) {
ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
if (this.boundSql.hasAdditionalParameter(propertyName)) {
value = this.boundSql.getAdditionalParameter(propertyName);
} else if (this.parameterObject == null) {
value = null;
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
value = this.parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (SQLException | TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
}
}
}
}
}
}
Default ParameterHandler 구현 클래스 를 통 해 우 리 는 ParameterHandler 를 통 해 다음 중요 한 클래스 인 Mapped Statement 을 포함 하여 어떤 속성 과 방법 을 얻 을 수 있 는 지 알 고 있 습 니 다.2.MappedStatement
MyBatis 의 mapper 파일 에 있 는 모든 select/update/insert/delete 탭 은 해상도 기 에서 대응 하 는 Mapped Statement 대상,즉 Mapped Statement 대상 이 SQL 문 구 를 설명 하 는 것 으로 분 석 됩 니 다.MappedStatement 대상 속성 은 다음 과 같 습 니 다.
// mapper
private String resource;
// mybatis , jdbc
private Configuration configuration;
// id , :com.example.mybatis.dao.UserMapper.selectByExample
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
// sql :select、update、delete、insert
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
이 예 에서 Mapped Statement 대상 의 sqlCommandType 을 통 해 현재 sql 유형 이 insert,update 라 고 판단 하여 다음 작업 을 진행 합 니 다.4.StatementHandler 를 통 해 SQL 을 고 칩 니 다.
StatementHandler 는 매개 변 수 를 설정 하고 Statement 결과 집합 을 List 집합 으로 변환 하 는 등 JDBC Statement 작업 을 봉인 하 는 데 사 용 됩 니 다.
구현 코드 는 다음 과 같 습 니 다:
주석 태그 삭제
@Target({ElementType.METHOD}) //
@Retention(RetentionPolicy.RUNTIME) //
@Documented //
public @interface DeletedAt {
boolean has() default true;
}
Dao 층 에 삭제 주 해 를 추가 합 니 다.false 일 때 삭제 플래그 를 추가 하지 않 습 니 다.
@Mapper
public interface AdminProjectDao {
@DeletedAt(has = false)
List<AdminProjectPo> selectProjects(AdminProjectPo po);
}
차단 기 는 주석 표 시 를 삭제 하여 삭제 표 지 를 추가 할 지 여 부 를 판단 합 니 다.
@Component
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
})
public class MyBatisInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof StatementHandler) {
System.out.println("StatementHandler");
checkHasDeletedAtField(invocation);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
*
*
* @param invocation
* @throws Throwable
*/
private void checkHasDeletedAtField(Invocation invocation) throws Throwable {
System.out.println("checkHasDeletedAtField");
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// MetaObject
MetaObject metaObject = MetaObject.forObject(
statementHandler,
SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
new DefaultReflectorFactory());
// mappedStatement
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// sql
if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) {
//
DeletedAt annotation = null;
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
Class<?> aClass = Class.forName(className);
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
declaredMethod.setAccessible(true);
// , DeletedAt
if (methodName.equals(declaredMethod.getName()) && declaredMethod.isAnnotationPresent(DeletedAt.class)) {
annotation = declaredMethod.getAnnotation(DeletedAt.class);
}
}
// true( true) mysql
if (annotation == null || annotation.has()) {
BoundSql boundSql = statementHandler.getBoundSql();
// sql
String sql = boundSql.getSql();
// sql
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
String newSql = sql.replaceAll("9=9", "9=9 and deleted_at is null ");
field.set(boundSql, newSql);
}
}
}
}
SQL 문장 교체 에 있어 서 교체 할 내용 을 식별 할 수 있어 야 하기 때문에 xml 의 sql 문장 에 특수 표지 인'9=9'를 추가 합 니 다.이 표 지 는 원래 SQL 의 실행 결과 에 영향 을 주지 않 고 서로 다른 여과 조건 에 서로 다른 표 지 를 설정 할 수 있 으 며 비교적 교묘 한 교체 방식 입 니 다.이상 은 MyBatis 차단기 의 원리 와 사용 에 대한 상세 한 내용 입 니 다.MyBatis 차단기 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
MyBatis + SpringBoot로 CRUD 앱 만들기 ※ 불필요한 것은 배 ※ 1/2MyBatis를 사용하여 ToDo 목록을 만듭니다. 할 일 등록 (블랭크를 등록 할 수 없음) 할 일보기 할 일 변경 할 일 지우기 SpringBoot의 CRUD가 가능한 책은 있지만 MyBatis를 사용한 것은 적...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.