Selaa lähdekoodia

喵神来玩啊,来快活啊

miemie 7 vuotta sitten
vanhempi
commit
6c1d691b5e

+ 2 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

@@ -59,7 +59,6 @@ import org.apache.ibatis.annotations.TypeDiscriminator;
 import org.apache.ibatis.annotations.Update;
 import org.apache.ibatis.annotations.UpdateProvider;
 import org.apache.ibatis.binding.BindingException;
-import org.apache.ibatis.binding.MapperMethod.ParamMap;
 import org.apache.ibatis.builder.BuilderException;
 import org.apache.ibatis.builder.IncompleteElementException;
 import org.apache.ibatis.builder.MapperBuilderAssistant;
@@ -93,6 +92,7 @@ import org.apache.ibatis.type.TypeHandler;
 import org.apache.ibatis.type.UnknownTypeHandler;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.override.PageMapperMethod;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 
 
@@ -416,7 +416,7 @@ public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
                     parameterType = currentParameterType;
                 } else {
                     // issue #135
-                    parameterType = ParamMap.class;
+                    parameterType = PageMapperMethod.ParamMap.class;
                 }
             }
         }

+ 4 - 4
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperRegistry.java

@@ -21,11 +21,11 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.ibatis.binding.BindingException;
-import org.apache.ibatis.binding.MapperProxyFactory;
 import org.apache.ibatis.binding.MapperRegistry;
 import org.apache.ibatis.session.Configuration;
 import org.apache.ibatis.session.SqlSession;
 
+import com.baomidou.mybatisplus.core.override.PageMapperProxyFactory;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 
 /**
@@ -38,7 +38,7 @@ import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
  */
 public class MybatisMapperRegistry extends MapperRegistry {
 
-    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
+    private final Map<Class<?>, PageMapperProxyFactory<?>> knownMappers = new HashMap<>();
     private final Configuration config;
 
     public MybatisMapperRegistry(Configuration config) {
@@ -50,7 +50,7 @@ public class MybatisMapperRegistry extends MapperRegistry {
 
     @Override
     public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
-        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
+        final PageMapperProxyFactory<T> mapperProxyFactory = (PageMapperProxyFactory<T>) knownMappers.get(type);
         if (mapperProxyFactory == null) {
             throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
         }
@@ -77,7 +77,7 @@ public class MybatisMapperRegistry extends MapperRegistry {
             }
             boolean loadCompleted = false;
             try {
-                knownMappers.put(type, new MapperProxyFactory<>(type));
+                knownMappers.put(type, new PageMapperProxyFactory<>(type));
                 // It's important that the type is added before the parser is run
                 // otherwise the binding may automatically be attempted by the
                 // mapper parser. If the type is already known, it won't try.

+ 7 - 5
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java

@@ -15,15 +15,17 @@
  */
 package com.baomidou.mybatisplus.core.mapper;
 
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.session.RowBounds;
-
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.RowBounds;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.pagination.Page;
+
 /**
  * <p>
  * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
@@ -219,7 +221,7 @@ public interface BaseMapper<T> {
      * @param queryWrapper 实体对象封装操作类(可以为 null)
      * @return List<T>
      */
-    List<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> queryWrapper);
+    Page<T> selectPage(RowBounds rowBounds, @Param("ew") Wrapper<T> queryWrapper);
 
     /**
      * <p>

+ 376 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/PageMapperMethod.java

@@ -0,0 +1,376 @@
+package com.baomidou.mybatisplus.core.override;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.annotations.Flush;
+import org.apache.ibatis.annotations.MapKey;
+import org.apache.ibatis.binding.BindingException;
+import org.apache.ibatis.cursor.Cursor;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.mapping.StatementType;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.ParamNameResolver;
+import org.apache.ibatis.reflection.TypeParameterResolver;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+import org.apache.ibatis.session.SqlSession;
+
+import com.baomidou.mybatisplus.core.pagination.Page;
+
+/**
+ * @author ming
+ * @since 2018/6/9
+ */
+public class PageMapperMethod {
+
+    private final PageMapperMethod.SqlCommand command;
+    private final PageMapperMethod.MethodSignature method;
+
+    public PageMapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
+        this.command = new PageMapperMethod.SqlCommand(config, mapperInterface, method);
+        this.method = new PageMapperMethod.MethodSignature(config, mapperInterface, method);
+    }
+
+    @SuppressWarnings("unchecked")
+    public Object execute(SqlSession sqlSession, Object[] args) {
+        Object result;
+        switch (command.getType()) {
+            case INSERT: {
+                Object param = method.convertArgsToSqlCommandParam(args);
+                result = rowCountResult(sqlSession.insert(command.getName(), param));
+                break;
+            }
+            case UPDATE: {
+                Object param = method.convertArgsToSqlCommandParam(args);
+                result = rowCountResult(sqlSession.update(command.getName(), param));
+                break;
+            }
+            case DELETE: {
+                Object param = method.convertArgsToSqlCommandParam(args);
+                result = rowCountResult(sqlSession.delete(command.getName(), param));
+                break;
+            }
+            case SELECT:
+                if (method.returnsVoid() && method.hasResultHandler()) {
+                    executeWithResultHandler(sqlSession, args);
+                    result = null;
+                } else if (method.returnsMany()) {
+                    result = executeForMany(sqlSession, args);
+                } else if (method.returnsMap()) {
+                    result = executeForMap(sqlSession, args);
+                } else if (method.returnsCursor()) {
+                    result = executeForCursor(sqlSession, args);
+                } else {
+                    Object param = method.convertArgsToSqlCommandParam(args);
+                    if (Page.class.isAssignableFrom(method.getReturnType()) && args != null
+                        && Page.class.isAssignableFrom(args[0].getClass())) {
+                        List<Object> o = (List<Object>) executeForMany2(sqlSession, args);
+                        result = ((Page) args[0]).setRecords(o);
+                    } else {
+                        result = sqlSession.selectOne(command.getName(), param);
+                    }
+                }
+                break;
+            case FLUSH:
+                result = sqlSession.flushStatements();
+                break;
+            default:
+                throw new BindingException("Unknown execution method for: " + command.getName());
+        }
+        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
+            throw new BindingException("Mapper method '" + command.getName()
+                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
+        }
+        return result;
+    }
+
+    private <E> Object executeForMany2(SqlSession sqlSession, Object[] args) {
+        List<E> result;
+        Object param = method.convertArgsToSqlCommandParam(args);
+        if (method.hasRowBounds()) {
+            RowBounds rowBounds = method.extractRowBounds(args);
+            result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
+        } else {
+            result = sqlSession.<E>selectList(command.getName(), param);
+        }
+        return result;
+    }
+
+    private Object rowCountResult(int rowCount) {
+        final Object result;
+        if (method.returnsVoid()) {
+            result = null;
+        } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
+            result = rowCount;
+        } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
+            result = (long) rowCount;
+        } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
+            result = rowCount > 0;
+        } else {
+            throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
+        }
+        return result;
+    }
+
+    private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
+        MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
+        if (!StatementType.CALLABLE.equals(ms.getStatementType())
+            && void.class.equals(ms.getResultMaps().get(0).getType())) {
+            throw new BindingException("method " + command.getName()
+                + " needs either a @ResultMap annotation, a @ResultType annotation,"
+                + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
+        }
+        Object param = method.convertArgsToSqlCommandParam(args);
+        if (method.hasRowBounds()) {
+            RowBounds rowBounds = method.extractRowBounds(args);
+            sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
+        } else {
+            sqlSession.select(command.getName(), param, method.extractResultHandler(args));
+        }
+    }
+
+    private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
+        List<E> result;
+        Object param = method.convertArgsToSqlCommandParam(args);
+        if (method.hasRowBounds()) {
+            RowBounds rowBounds = method.extractRowBounds(args);
+            result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
+        } else {
+            result = sqlSession.<E>selectList(command.getName(), param);
+        }
+        // issue #510 Collections & arrays support
+        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
+            if (method.getReturnType().isArray()) {
+                return convertToArray(result);
+            } else {
+                return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
+            }
+        }
+        return result;
+    }
+
+    private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
+        Cursor<T> result;
+        Object param = method.convertArgsToSqlCommandParam(args);
+        if (method.hasRowBounds()) {
+            RowBounds rowBounds = method.extractRowBounds(args);
+            result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
+        } else {
+            result = sqlSession.<T>selectCursor(command.getName(), param);
+        }
+        return result;
+    }
+
+    private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
+        Object collection = config.getObjectFactory().create(method.getReturnType());
+        MetaObject metaObject = config.newMetaObject(collection);
+        metaObject.addAll(list);
+        return collection;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <E> Object convertToArray(List<E> list) {
+        Class<?> arrayComponentType = method.getReturnType().getComponentType();
+        Object array = Array.newInstance(arrayComponentType, list.size());
+        if (arrayComponentType.isPrimitive()) {
+            for (int i = 0; i < list.size(); i++) {
+                Array.set(array, i, list.get(i));
+            }
+            return array;
+        } else {
+            return list.toArray((E[]) array);
+        }
+    }
+
+    private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
+        Map<K, V> result;
+        Object param = method.convertArgsToSqlCommandParam(args);
+        if (method.hasRowBounds()) {
+            RowBounds rowBounds = method.extractRowBounds(args);
+            result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
+        } else {
+            result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
+        }
+        return result;
+    }
+
+    public static class ParamMap<V> extends HashMap<String, V> {
+
+        private static final long serialVersionUID = -2212268410512043556L;
+
+        @Override
+        public V get(Object key) {
+            if (!super.containsKey(key)) {
+                throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
+            }
+            return super.get(key);
+        }
+
+    }
+
+    public static class SqlCommand {
+
+        private final String name;
+        private final SqlCommandType type;
+
+        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
+            final String methodName = method.getName();
+            final Class<?> declaringClass = method.getDeclaringClass();
+            MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
+                configuration);
+            if (ms == null) {
+                if (method.getAnnotation(Flush.class) != null) {
+                    name = null;
+                    type = SqlCommandType.FLUSH;
+                } else {
+                    throw new BindingException("Invalid bound statement (not found): "
+                        + mapperInterface.getName() + "." + methodName);
+                }
+            } else {
+                name = ms.getId();
+                type = ms.getSqlCommandType();
+                if (type == SqlCommandType.UNKNOWN) {
+                    throw new BindingException("Unknown execution method for: " + name);
+                }
+            }
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public SqlCommandType getType() {
+            return type;
+        }
+
+        private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
+                                                       Class<?> declaringClass, Configuration configuration) {
+            String statementId = mapperInterface.getName() + "." + methodName;
+            if (configuration.hasStatement(statementId)) {
+                return configuration.getMappedStatement(statementId);
+            } else if (mapperInterface.equals(declaringClass)) {
+                return null;
+            }
+            for (Class<?> superInterface : mapperInterface.getInterfaces()) {
+                if (declaringClass.isAssignableFrom(superInterface)) {
+                    MappedStatement ms = resolveMappedStatement(superInterface, methodName,
+                        declaringClass, configuration);
+                    if (ms != null) {
+                        return ms;
+                    }
+                }
+            }
+            return null;
+        }
+    }
+
+    public static class MethodSignature {
+
+        private final boolean returnsMany;
+        private final boolean returnsMap;
+        private final boolean returnsVoid;
+        private final boolean returnsCursor;
+        private final Class<?> returnType;
+        private final String mapKey;
+        private final Integer resultHandlerIndex;
+        private final Integer rowBoundsIndex;
+        private final ParamNameResolver paramNameResolver;
+
+        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
+            Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
+            if (resolvedReturnType instanceof Class<?>) {
+                this.returnType = (Class<?>) resolvedReturnType;
+            } else if (resolvedReturnType instanceof ParameterizedType) {
+                this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
+            } else {
+                this.returnType = method.getReturnType();
+            }
+            this.returnsVoid = void.class.equals(this.returnType);
+            this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
+            this.returnsCursor = Cursor.class.equals(this.returnType);
+            this.mapKey = getMapKey(method);
+            this.returnsMap = this.mapKey != null;
+            this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
+            this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
+            this.paramNameResolver = new ParamNameResolver(configuration, method);
+        }
+
+        public Object convertArgsToSqlCommandParam(Object[] args) {
+            return paramNameResolver.getNamedParams(args);
+        }
+
+        public boolean hasRowBounds() {
+            return rowBoundsIndex != null;
+        }
+
+        public RowBounds extractRowBounds(Object[] args) {
+            return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
+        }
+
+        public boolean hasResultHandler() {
+            return resultHandlerIndex != null;
+        }
+
+        public ResultHandler extractResultHandler(Object[] args) {
+            return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
+        }
+
+        public String getMapKey() {
+            return mapKey;
+        }
+
+        public Class<?> getReturnType() {
+            return returnType;
+        }
+
+        public boolean returnsMany() {
+            return returnsMany;
+        }
+
+        public boolean returnsMap() {
+            return returnsMap;
+        }
+
+        public boolean returnsVoid() {
+            return returnsVoid;
+        }
+
+        public boolean returnsCursor() {
+            return returnsCursor;
+        }
+
+        private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
+            Integer index = null;
+            final Class<?>[] argTypes = method.getParameterTypes();
+            for (int i = 0; i < argTypes.length; i++) {
+                if (paramType.isAssignableFrom(argTypes[i])) {
+                    if (index == null) {
+                        index = i;
+                    } else {
+                        throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
+                    }
+                }
+            }
+            return index;
+        }
+
+        private String getMapKey(Method method) {
+            String mapKey = null;
+            if (Map.class.isAssignableFrom(method.getReturnType())) {
+                final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
+                if (mapKeyAnnotation != null) {
+                    mapKey = mapKeyAnnotation.value();
+                }
+            }
+            return mapKey;
+        }
+    }
+}

+ 80 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/PageMapperProxy.java

@@ -0,0 +1,80 @@
+package com.baomidou.mybatisplus.core.override;
+
+import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+import org.apache.ibatis.lang.UsesJava7;
+import org.apache.ibatis.reflection.ExceptionUtil;
+import org.apache.ibatis.session.SqlSession;
+
+/**
+ * @author ming
+ * @since 2018/6/9
+ */
+public class PageMapperProxy<T> implements InvocationHandler, Serializable {
+
+    private static final long serialVersionUID = -6424540398559729838L;
+    private final SqlSession sqlSession;
+    private final Class<T> mapperInterface;
+    private final Map<Method, PageMapperMethod> methodCache;
+
+    public PageMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, PageMapperMethod> methodCache) {
+        this.sqlSession = sqlSession;
+        this.mapperInterface = mapperInterface;
+        this.methodCache = methodCache;
+    }
+
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        try {
+            if (Object.class.equals(method.getDeclaringClass())) {
+                return method.invoke(this, args);
+            } else if (isDefaultMethod(method)) {
+                return invokeDefaultMethod(proxy, method, args);
+            }
+        } catch (Throwable t) {
+            throw ExceptionUtil.unwrapThrowable(t);
+        }
+        final PageMapperMethod mapperMethod = cachedMapperMethod(method);
+        return mapperMethod.execute(sqlSession, args);
+    }
+
+    private PageMapperMethod cachedMapperMethod(Method method) {
+        PageMapperMethod mapperMethod = methodCache.get(method);
+        if (mapperMethod == null) {
+            mapperMethod = new PageMapperMethod(mapperInterface, method, sqlSession.getConfiguration());
+            methodCache.put(method, mapperMethod);
+        }
+        return mapperMethod;
+    }
+
+    @UsesJava7
+    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
+        throws Throwable {
+        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
+            .getDeclaredConstructor(Class.class, int.class);
+        if (!constructor.isAccessible()) {
+            constructor.setAccessible(true);
+        }
+        final Class<?> declaringClass = method.getDeclaringClass();
+        return constructor
+            .newInstance(declaringClass,
+                MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
+                    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
+            .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
+    }
+
+    /**
+     * Backport of java.lang.reflect.Method#isDefault()
+     */
+    private boolean isDefaultMethod(Method method) {
+        return (method.getModifiers()
+            & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
+            && method.getDeclaringClass().isInterface();
+    }
+}

+ 40 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/PageMapperProxyFactory.java

@@ -0,0 +1,40 @@
+package com.baomidou.mybatisplus.core.override;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ibatis.session.SqlSession;
+
+/**
+ * @author ming
+ * @since 2018/6/9
+ */
+public class PageMapperProxyFactory<T> {
+
+    private final Class<T> mapperInterface;
+    private final Map<Method, PageMapperMethod> methodCache = new ConcurrentHashMap<>();
+
+    public PageMapperProxyFactory(Class<T> mapperInterface) {
+        this.mapperInterface = mapperInterface;
+    }
+
+    public Class<T> getMapperInterface() {
+        return mapperInterface;
+    }
+
+    public Map<Method, PageMapperMethod> getMethodCache() {
+        return methodCache;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected T newInstance(PageMapperProxy<T> mapperProxy) {
+        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
+    }
+
+    public T newInstance(SqlSession sqlSession) {
+        final PageMapperProxy<T> mapperProxy = new PageMapperProxy<T>(sqlSession, mapperInterface, methodCache);
+        return newInstance(mapperProxy);
+    }
+}