Skip to content

DATAJDBC-373 - Integration Mybatis with spring-data-jdbc,make mybatis can mixed with… #152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.springframework.data.jdbc.mybatis.support;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.lang.Nullable;

/**
* @author Songling.Dong
* Created on 2019/5/15.
*/
public interface MybatisContext {


@Nullable
default SqlSessionTemplate getSqlSessionTemplate() {
return null;
}

MybatisContext EMPTY = new MybatisContext() {


};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.springframework.data.jdbc.mybatis.support;

import org.springframework.data.annotation.QueryAnnotation;

import java.lang.annotation.*;

/**
* @author Songling.Dong
* Created on 2019/5/8.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@QueryAnnotation
@Documented
public @interface MybatisQuery {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.springframework.data.jdbc.mybatis.support;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethod;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* @author Songling.Dong
* Created on 2019/5/9.
*/
public class MybatisQueryMethod extends QueryMethod {

private final Method method;
private final Object mapper;

/**
* Creates a new {@link QueryMethod} from the given parameters. Looks up the correct query to use for following
* invocations of the method given.
*
* @param method must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @param factory must not be {@literal null}.
*/
public MybatisQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, Object mapper) {
super(method, metadata, factory);
this.method = method;
this.mapper = mapper;
}



Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException {
return method.invoke(mapper, args);
}

@Override
public boolean isModifyingQuery() {
return AnnotationUtils.findAnnotation(method, Modifying.class) != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.springframework.data.jdbc.mybatis.support;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.relational.core.mapping.event.Identifier;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.lang.Nullable;

import java.lang.reflect.InvocationTargetException;

/**
* @author Songling.Dong
* Created on 2019/5/9.
*/
public class MybatisRepositoryQuery implements RepositoryQuery {


private final ApplicationEventPublisher publisher;
private final RelationalMappingContext context;
private final MybatisQueryMethod mybatisQueryMethod;

public MybatisRepositoryQuery(ApplicationEventPublisher publisher, RelationalMappingContext context, MybatisQueryMethod mybatisQueryMethod) {
this.publisher = publisher;
this.context = context;
this.mybatisQueryMethod = mybatisQueryMethod;
}

@Override
public Object execute(Object[] parameters) {
try {
Object retVal = mybatisQueryMethod.invoke(parameters);
if (!mybatisQueryMethod.isModifyingQuery()) {
if (mybatisQueryMethod.isCollectionQuery() || mybatisQueryMethod.isStreamQuery()) {
publishAfterLoad((Iterable<?>) retVal);
} else {
publishAfterLoad(retVal);
}
}
return retVal;
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
} catch (IllegalAccessException e) {
throw new RuntimeException("invoke from mybatis mapper failed", e);
}
}

@Override
public MybatisQueryMethod getQueryMethod() {
return mybatisQueryMethod;
}


private <T> void publishAfterLoad(Iterable<T> all) {

for (T e : all) {
publishAfterLoad(e);
}
}

private <T> void publishAfterLoad(@Nullable T entity) {
//duplicate code.
if (entity != null && context.hasPersistentEntityFor(entity.getClass())) {

RelationalPersistentEntity<?> e = context.getRequiredPersistentEntity(entity.getClass());
Object identifier = e.getIdentifierAccessor(entity)
.getIdentifier();

if (identifier != null) {
publisher.publishEvent(new AfterLoadEvent(Identifier.of(identifier), entity));
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NonNullApi
package org.springframework.data.jdbc.mybatis.support;

import org.springframework.lang.NonNullApi;
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@
*/
package org.springframework.data.jdbc.repository.support;

import java.lang.reflect.Method;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.EntityRowMapper;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.mybatis.support.MybatisContext;
import org.springframework.data.jdbc.mybatis.support.MybatisQuery;
import org.springframework.data.jdbc.mybatis.support.MybatisQueryMethod;
import org.springframework.data.jdbc.mybatis.support.MybatisRepositoryQuery;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
Expand All @@ -34,6 +37,8 @@
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert;

import java.lang.reflect.Method;

/**
* {@link QueryLookupStrategy} for JDBC repositories. Currently only supports annotated queries.
*
Expand All @@ -45,83 +50,105 @@
*/
class JdbcQueryLookupStrategy implements QueryLookupStrategy {

private final ApplicationEventPublisher publisher;
private final RelationalMappingContext context;
private final JdbcConverter converter;
private final DataAccessStrategy accessStrategy;
private final QueryMappingConfiguration queryMappingConfiguration;
private final NamedParameterJdbcOperations operations;

/**
* Creates a new {@link JdbcQueryLookupStrategy} for the given {@link RelationalMappingContext},
* {@link DataAccessStrategy} and {@link QueryMappingConfiguration}.
*
* @param publisher must not be {@literal null}.
* @param context must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param accessStrategy must not be {@literal null}.
* @param queryMappingConfiguration must not be {@literal null}.
*/
JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, RelationalMappingContext context,
JdbcConverter converter, DataAccessStrategy accessStrategy, QueryMappingConfiguration queryMappingConfiguration,
NamedParameterJdbcOperations operations) {

Assert.notNull(publisher, "Publisher must not be null!");
Assert.notNull(context, "RelationalMappingContext must not be null!");
Assert.notNull(converter, "RelationalConverter must not be null!");
Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!");
Assert.notNull(queryMappingConfiguration, "RowMapperMap must not be null!");

this.publisher = publisher;
this.context = context;
this.converter = converter;
this.accessStrategy = accessStrategy;
this.queryMappingConfiguration = queryMappingConfiguration;
this.operations = operations;
}

/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {

JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory);

RowMapper<?> mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod);

return new JdbcRepositoryQuery(publisher, context, queryMethod, operations, mapper);
}

private RowMapper<?> createMapper(JdbcQueryMethod queryMethod) {

Class<?> returnedObjectType = queryMethod.getReturnedObjectType();

RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(returnedObjectType);

if (persistentEntity == null) {
return SingleColumnRowMapper.newInstance(returnedObjectType, converter.getConversionService());
}

return determineDefaultMapper(queryMethod);
}

private RowMapper<?> determineDefaultMapper(JdbcQueryMethod queryMethod) {

Class<?> domainType = queryMethod.getReturnedObjectType();
RowMapper<?> configuredQueryMapper = queryMappingConfiguration.getRowMapper(domainType);

if (configuredQueryMapper != null)
return configuredQueryMapper;

EntityRowMapper<?> defaultEntityRowMapper = new EntityRowMapper<>( //
context.getRequiredPersistentEntity(domainType), //
//
converter, //
accessStrategy);

return defaultEntityRowMapper;
}
private final ApplicationEventPublisher publisher;
private final RelationalMappingContext context;
private final JdbcConverter converter;
private final DataAccessStrategy accessStrategy;
private final QueryMappingConfiguration queryMappingConfiguration;
private final NamedParameterJdbcOperations operations;

private MybatisContext mybatisContent;

/**
* Creates a new {@link JdbcQueryLookupStrategy} for the given {@link RelationalMappingContext},
* {@link DataAccessStrategy} and {@link QueryMappingConfiguration}.
*
* @param publisher must not be {@literal null}.
* @param context must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param accessStrategy must not be {@literal null}.
* @param queryMappingConfiguration must not be {@literal null}.
*/
JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, RelationalMappingContext context,
JdbcConverter converter, DataAccessStrategy accessStrategy, QueryMappingConfiguration queryMappingConfiguration,
NamedParameterJdbcOperations operations) {

Assert.notNull(publisher, "Publisher must not be null!");
Assert.notNull(context, "RelationalMappingContext must not be null!");
Assert.notNull(converter, "RelationalConverter must not be null!");
Assert.notNull(accessStrategy, "DataAccessStrategy must not be null!");
Assert.notNull(queryMappingConfiguration, "RowMapperMap must not be null!");

this.publisher = publisher;
this.context = context;
this.converter = converter;
this.accessStrategy = accessStrategy;
this.queryMappingConfiguration = queryMappingConfiguration;
this.operations = operations;
}

/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {


if (method.isAnnotationPresent(MybatisQuery.class)) {
return createMybatisRepositoryQuery(method, repositoryMetadata, projectionFactory);
} else {
JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory);

RowMapper<?> mapper = queryMethod.isModifyingQuery() ? null : createMapper(queryMethod);

return new JdbcRepositoryQuery(publisher, context, queryMethod, operations, mapper);
}

}

private RepositoryQuery createMybatisRepositoryQuery(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory projectionFactory) {
SqlSessionTemplate sqlSession = mybatisContent.getSqlSessionTemplate();
if (sqlSession == null) {
throw new IllegalStateException(String.format("You have annotated @MybatisQuery on method:%s ,but no org.mybatis.spring.SqlSessionTemplate provided.", method.getName()));
}
Object mapper = sqlSession.getMapper(method.getDeclaringClass());
MybatisQueryMethod mybatisQueryMethod = new MybatisQueryMethod(method, repositoryMetadata, projectionFactory, mapper);
return new MybatisRepositoryQuery(publisher, context, mybatisQueryMethod);
}

private RowMapper<?> createMapper(JdbcQueryMethod queryMethod) {

Class<?> returnedObjectType = queryMethod.getReturnedObjectType();

RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(returnedObjectType);

if (persistentEntity == null) {
return SingleColumnRowMapper.newInstance(returnedObjectType, converter.getConversionService());
}

return determineDefaultMapper(queryMethod);
}

private RowMapper<?> determineDefaultMapper(JdbcQueryMethod queryMethod) {

Class<?> domainType = queryMethod.getReturnedObjectType();
RowMapper<?> configuredQueryMapper = queryMappingConfiguration.getRowMapper(domainType);

if (configuredQueryMapper != null)
return configuredQueryMapper;

EntityRowMapper<?> defaultEntityRowMapper = new EntityRowMapper<>( //
context.getRequiredPersistentEntity(domainType), //
//
converter, //
accessStrategy);

return defaultEntityRowMapper;
}

public void setMybatisContext(MybatisContext mybatisContent) {
this.mybatisContent = mybatisContent;
}
}
Loading