mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-03 11:24:34 +08:00
backup
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
@@ -50,8 +52,8 @@ public interface Authentication extends Serializable {
|
||||
* @see Optional
|
||||
* @see AuthenticationHolder
|
||||
*/
|
||||
static Optional<Authentication> current() {
|
||||
return Optional.ofNullable(AuthenticationHolder.get());
|
||||
static Mono<Authentication> current() {
|
||||
return AuthenticationHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import org.hswebframework.web.ThreadLocalUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -26,6 +28,7 @@ import java.util.Objects;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 权限获取器,用于静态方式获取当前登录用户的权限信息.
|
||||
@@ -45,31 +48,23 @@ import java.util.function.Function;
|
||||
public final class AuthenticationHolder {
|
||||
private static final List<AuthenticationSupplier> suppliers = new ArrayList<>();
|
||||
|
||||
private static final String CURRENT_USER_ID_KEY = Authentication.class.getName() + "_current_id";
|
||||
|
||||
private static final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
private static Authentication get(Function<AuthenticationSupplier, Authentication> function) {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
return suppliers.stream()
|
||||
.map(function)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
private static Mono<Authentication> get(Function<AuthenticationSupplier, Mono<Authentication>> function) {
|
||||
|
||||
return Flux.concat(suppliers.stream()
|
||||
.map(function)
|
||||
.collect(Collectors.toList()))
|
||||
.reduceWith(CompositeAuthentication::new, CompositeAuthentication::merge)
|
||||
.filter(CompositeAuthentication::isNotEmpty)
|
||||
.map(Authentication.class::cast);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前登录的用户权限信息
|
||||
*/
|
||||
public static Authentication get() {
|
||||
String currentId = ThreadLocalUtils.get(CURRENT_USER_ID_KEY);
|
||||
if (currentId != null) {
|
||||
return get(currentId);
|
||||
}
|
||||
public static Mono<Authentication> get() {
|
||||
|
||||
return get(AuthenticationSupplier::get);
|
||||
}
|
||||
|
||||
@@ -79,7 +74,7 @@ public final class AuthenticationHolder {
|
||||
* @param userId 用户ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
public static Authentication get(String userId) {
|
||||
public static Mono<Authentication> get(String userId) {
|
||||
return get(supplier -> supplier.get(userId));
|
||||
}
|
||||
|
||||
@@ -97,7 +92,4 @@ public final class AuthenticationHolder {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCurrentUserId(String id) {
|
||||
ThreadLocalUtils.put(AuthenticationHolder.CURRENT_USER_ID_KEY, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationInitializeEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationInitializeEvent;
|
||||
|
||||
/**
|
||||
* 授权信息初始化服务接口,使用该接口初始化用的权限信息
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,7 +38,7 @@ public interface AuthenticationManager {
|
||||
* @param request 授权请求
|
||||
* @return 授权成功则返回用户权限信息
|
||||
*/
|
||||
Authentication authenticate(AuthenticationRequest request);
|
||||
Mono<Authentication> authenticate(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取权限信息
|
||||
@@ -44,7 +46,7 @@ public interface AuthenticationManager {
|
||||
* @param userId 用户ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
Authentication getByUserId(String userId);
|
||||
Mono<Authentication> getByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 同步授权信息,在调用了{@link Authentication#setAttribute(String, Serializable)}或者
|
||||
@@ -56,5 +58,5 @@ public interface AuthenticationManager {
|
||||
* @param authentication 要同步的权限信息
|
||||
* @return 同步后的权限信息
|
||||
*/
|
||||
Authentication sync(Authentication authentication);
|
||||
Mono<Authentication> sync(Authentication authentication);
|
||||
}
|
||||
|
||||
@@ -44,17 +44,7 @@ public interface AuthenticationPredicate extends Predicate<Authentication> {
|
||||
return (t) -> test(t) || other.test(t);
|
||||
}
|
||||
|
||||
default boolean test() {
|
||||
return Authentication.current()
|
||||
.map(this::test)
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
default void assertHas() {
|
||||
if (!test()) {
|
||||
throw new AccessDenyException();
|
||||
}
|
||||
}
|
||||
|
||||
default void assertHas(Authentication authentication) {
|
||||
if (!test(authentication)) {
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -25,6 +27,6 @@ import java.util.function.Supplier;
|
||||
* @see Authentication
|
||||
* @see AuthenticationHolder
|
||||
*/
|
||||
public interface AuthenticationSupplier extends Supplier<Authentication> {
|
||||
Authentication get(String userId);
|
||||
public interface AuthenticationSupplier extends Supplier<Mono<Authentication>> {
|
||||
Mono<Authentication> get(String userId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CompositeAuthentication implements Authentication {
|
||||
|
||||
private Map<String, Authentication> userAuthentication = new ConcurrentHashMap<>();
|
||||
|
||||
private String currentUser;
|
||||
|
||||
public boolean isEmpty() {
|
||||
return userAuthentication.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isNotEmpty() {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
|
||||
return userAuthentication
|
||||
.get(currentUser)
|
||||
.getUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Role> getRoles() {
|
||||
return userAuthentication.values()
|
||||
.stream()
|
||||
.map(Authentication::getRoles)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return userAuthentication.values()
|
||||
.stream()
|
||||
.map(Authentication::getPermissions)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> Optional<T> getAttribute(String name) {
|
||||
return userAuthentication.values()
|
||||
.stream()
|
||||
.map(a -> a.<T>getAttribute(name))
|
||||
.filter(Optional::isPresent)
|
||||
.findAny()
|
||||
.flatMap(Function.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Serializable object) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Map<String, Serializable> attributes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> T removeAttributes(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Serializable> getAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompositeAuthentication merge(Authentication authentication) {
|
||||
String userId = authentication.getUser().getId();
|
||||
if (currentUser == null) {
|
||||
currentUser = userId;
|
||||
}
|
||||
userAuthentication.put(userId, authentication);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.hswebframework.web.authorization.define;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
/**
|
||||
* 授权事件
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.hswebframework.web.authorization.listener.event;
|
||||
package org.hswebframework.web.authorization.events;
|
||||
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.HandleType;
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.authorization.token;
|
||||
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
@@ -19,6 +20,6 @@ public interface ThirdPartAuthenticationManager {
|
||||
* @param userId 用户ID
|
||||
* @return 权限信息
|
||||
*/
|
||||
Authentication getByUserId(String userId);
|
||||
Mono<Authentication> getByUserId(String userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package org.hswebframework.web.authorization.token;
|
||||
|
||||
import org.hswebframework.web.ThreadLocalUtils;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.AuthenticationManager;
|
||||
import org.hswebframework.web.authorization.AuthenticationSupplier;
|
||||
import org.hswebframework.web.context.ContextKey;
|
||||
import org.hswebframework.web.context.ContextUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
@@ -32,14 +33,14 @@ public class UserTokenAuthenticationSupplier implements AuthenticationSupplier {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication get(String userId) {
|
||||
public Mono<Authentication> get(String userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
return get(this.defaultAuthenticationManager, userId);
|
||||
}
|
||||
|
||||
protected Authentication get(ThirdPartAuthenticationManager authenticationManager, String userId) {
|
||||
protected Mono<Authentication> get(ThirdPartAuthenticationManager authenticationManager, String userId) {
|
||||
if (null == userId) {
|
||||
return null;
|
||||
}
|
||||
@@ -49,7 +50,7 @@ public class UserTokenAuthenticationSupplier implements AuthenticationSupplier {
|
||||
return authenticationManager.getByUserId(userId);
|
||||
}
|
||||
|
||||
protected Authentication get(AuthenticationManager authenticationManager, String userId) {
|
||||
protected Mono<Authentication> get(AuthenticationManager authenticationManager, String userId) {
|
||||
if (null == userId) {
|
||||
return null;
|
||||
}
|
||||
@@ -59,19 +60,14 @@ public class UserTokenAuthenticationSupplier implements AuthenticationSupplier {
|
||||
return authenticationManager.getByUserId(userId);
|
||||
}
|
||||
|
||||
protected UserToken getCurrentUserToken() {
|
||||
return UserTokenHolder.currentToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication get() {
|
||||
return ThreadLocalUtils.get(Authentication.class.getName(), () ->
|
||||
Optional.ofNullable(getCurrentUserToken())
|
||||
.filter(UserToken::validate) //验证token,如果不是正常状态,将会抛出异常
|
||||
.map(token ->
|
||||
get(thirdPartAuthenticationManager
|
||||
.get(token.getType()), token.getUserId())
|
||||
)
|
||||
.orElse(null));
|
||||
public Mono<Authentication> get() {
|
||||
return ContextUtils.currentContext()
|
||||
.flatMap(context ->
|
||||
context.get(ContextKey.of(UserToken.class))
|
||||
.filter(UserToken::validate)
|
||||
.map(token -> get(thirdPartAuthenticationManager.get(token.getType()), token.getUserId()))
|
||||
.orElseGet(Mono::empty));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.hswebframework.web.authorization.token.event;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.hswebframework.web.authorization.token.event;
|
||||
|
||||
import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
public class UserTokenCreatedEvent extends ApplicationEvent implements AuthorizationEvent {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.hswebframework.web.authorization.token.event;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package org.hswebframework.web.authorization;
|
||||
|
||||
import org.hswebframework.web.authorization.builder.AuthenticationBuilder;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
||||
import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationBuilder;
|
||||
import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
|
||||
import org.hswebframework.web.authorization.token.*;
|
||||
import org.hswebframework.web.context.ContextKey;
|
||||
import org.hswebframework.web.context.ContextUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hswebframework.web.context.ContextUtils.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class AuthenticationTests {
|
||||
@@ -54,7 +60,7 @@ public class AuthenticationTests {
|
||||
assertEquals(authentication.getPermissions().size(), 1);
|
||||
assertTrue(authentication.hasPermission("user-manager"));
|
||||
assertTrue(authentication.hasPermission("user-manager", "get"));
|
||||
assertTrue(!authentication.hasPermission("user-manager", "delete"));
|
||||
assertFalse(authentication.hasPermission("user-manager", "delete"));
|
||||
|
||||
boolean has = AuthenticationPredicate.has("permission:user-manager")
|
||||
.or(AuthenticationPredicate.role("admin-role"))
|
||||
@@ -95,21 +101,21 @@ public class AuthenticationTests {
|
||||
//初始化权限管理器,用于获取用户的权限信息
|
||||
AuthenticationManager authenticationManager = new AuthenticationManager() {
|
||||
@Override
|
||||
public Authentication authenticate(AuthenticationRequest request) {
|
||||
public Mono<Authentication> authenticate(AuthenticationRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication getByUserId(String userId) {
|
||||
public Mono<Authentication> getByUserId(String userId) {
|
||||
if (userId.equals("admin")) {
|
||||
return authentication;
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
return null;
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication sync(Authentication authentication) {
|
||||
return authentication;
|
||||
public Mono<Authentication> sync(Authentication authentication) {
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
};
|
||||
AuthenticationHolder.addSupplier(new UserTokenAuthenticationSupplier(authenticationManager));
|
||||
@@ -117,11 +123,16 @@ public class AuthenticationTests {
|
||||
//绑定用户token
|
||||
UserTokenManager userTokenManager = new DefaultUserTokenManager();
|
||||
UserToken token = userTokenManager.signIn("test", "token-test", "admin", -1);
|
||||
UserTokenHolder.setCurrent(token);
|
||||
|
||||
//获取当前登录用户
|
||||
Authentication current = Authentication.current().orElseThrow(UnAuthorizedException::new);
|
||||
Assert.assertEquals(current.getUser().getId(), "admin");
|
||||
Authentication
|
||||
.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.subscriberContext(acceptContext(ctx->ctx.put(ContextKey.of(UserToken.class),token)))
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("admin")
|
||||
.verifyComplete();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.hswebframework.web.authorization.define.AuthorizeDefinition;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.HandleType;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizingHandleBeforeEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizingHandleBeforeEvent;
|
||||
import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -4,7 +4,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.HandleType;
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizingHandleBeforeEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizingHandleBeforeEvent;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.hswebframework.web.WebUtil;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.AuthenticationManager;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.authorization.listener.event.*;
|
||||
import org.hswebframework.web.authorization.events.*;
|
||||
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.logging.AccessLogger;
|
||||
@@ -37,7 +37,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.hswebframework.web.controller.message.ResponseMessage.ok;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.authorization.basic.web;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationSuccessEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationSuccessEvent;
|
||||
import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenHolder;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
@@ -15,7 +16,7 @@ import java.util.List;
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see org.springframework.context.ApplicationEvent
|
||||
* @see org.hswebframework.web.authorization.listener.event.AuthorizationEvent
|
||||
* @see AuthorizationEvent
|
||||
* @see UserTokenManager
|
||||
* @see UserTokenGenerator
|
||||
* @since 3.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.hswebframework.web.authorization.basic.web;
|
||||
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizationExitEvent;
|
||||
import org.hswebframework.web.authorization.events.AuthorizationExitEvent;
|
||||
import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenHolder;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.hswebframework.web.authorization.basic.define.EmptyAuthorizeDefinitio
|
||||
import org.hswebframework.web.authorization.define.AuthorizeDefinition
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext
|
||||
import org.hswebframework.web.authorization.define.HandleType
|
||||
import org.hswebframework.web.authorization.listener.event.AuthorizingHandleBeforeEvent
|
||||
import org.hswebframework.web.authorization.events.AuthorizingHandleBeforeEvent
|
||||
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest
|
||||
import org.hswebframework.web.boost.aop.context.MethodInterceptorContext
|
||||
import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization-oauth2</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization-oauth2</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization-oauth2</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<!--<relativePath>../../pom.xml</relativePath>-->
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-authorization</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-framework</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-framework</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -65,19 +65,19 @@ public interface CreateController<E, PK, M> {
|
||||
if (entity instanceof RecordCreationEntity) {
|
||||
RecordCreationEntity creationEntity = (RecordCreationEntity) entity;
|
||||
creationEntity.setCreateTimeNow();
|
||||
creationEntity.setCreatorId(Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null));
|
||||
// creationEntity.setCreatorId(Authentication.current()
|
||||
// .map(Authentication::getUser)
|
||||
// .map(User::getId)
|
||||
// .orElse(null));
|
||||
}
|
||||
//修改人和修改时间
|
||||
if (entity instanceof RecordModifierEntity) {
|
||||
RecordModifierEntity creationEntity = (RecordModifierEntity) entity;
|
||||
creationEntity.setModifyTimeNow();
|
||||
creationEntity.setModifierId(Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null));
|
||||
// creationEntity.setModifierId(Authentication.current()
|
||||
// .map(Authentication::getUser)
|
||||
// .map(User::getId)
|
||||
// .orElse(null));
|
||||
}
|
||||
return ok(getService().insert(entity));
|
||||
}
|
||||
|
||||
35
hsweb-commons/hsweb-commons-crud/pom.xml
Normal file
35
hsweb-commons/hsweb-commons-crud/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-commons-crud</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-easy-orm-rdb</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class PagerResult<E> {
|
||||
private static final long serialVersionUID = -6171751136953308027L;
|
||||
|
||||
public static <E> PagerResult<E> empty() {
|
||||
return new PagerResult<>(0, new ArrayList<>());
|
||||
}
|
||||
|
||||
public static <E> PagerResult<E> of(int total, List<E> list) {
|
||||
return new PagerResult<>(total, list);
|
||||
}
|
||||
|
||||
public static <E> PagerResult<E> of(int total, List<E> list, QueryParamEntity entity) {
|
||||
PagerResult<E> pagerResult = new PagerResult<>(total, list);
|
||||
pagerResult.setPageIndex(entity.getThinkPageIndex());
|
||||
pagerResult.setPageSize(entity.getPageSize());
|
||||
return pagerResult;
|
||||
}
|
||||
|
||||
private int pageIndex;
|
||||
|
||||
private int pageSize;
|
||||
|
||||
private int total;
|
||||
|
||||
private List<E> data;
|
||||
|
||||
public PagerResult() {
|
||||
}
|
||||
|
||||
public PagerResult(int total, List<E> data) {
|
||||
this.total = total;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.hswebframework.ezorm.core.NestConditional;
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 查询参数实体,使用<a href="https://github.com/hs-web/hsweb-easy-orm">easyorm</a>进行动态查询参数构建<br>
|
||||
* 可通过静态方法创建:<br>
|
||||
* 如:
|
||||
* <code>
|
||||
* QueryParamEntity.of("id",id);
|
||||
* </code>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see QueryParam
|
||||
* @since 3.0
|
||||
*/
|
||||
public class QueryParamEntity extends QueryParam {
|
||||
|
||||
private static final long serialVersionUID = 8097500947924037523L;
|
||||
|
||||
@Getter
|
||||
private String termExpression;
|
||||
|
||||
/**
|
||||
* 创建一个空的查询参数实体,该实体无任何参数.
|
||||
*
|
||||
* @return 无条件的参数实体
|
||||
*/
|
||||
public static QueryParamEntity of() {
|
||||
return new QueryParamEntity();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see this#of(String, Object)
|
||||
*/
|
||||
public static QueryParamEntity of(String field, Object value) {
|
||||
return of().and(field, TermType.eq, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.4
|
||||
*/
|
||||
public static <T> Query<T, QueryParamEntity> newQuery() {
|
||||
return Query.of(new QueryParamEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.4
|
||||
*/
|
||||
public <T> Query<T, QueryParamEntity> toQuery() {
|
||||
return Query.of(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将已有的条件包装到一个嵌套的条件里,并返回一个Query对象.例如:
|
||||
* <pre>
|
||||
* entity.toNestQuery().and("userId",userId);
|
||||
* </pre>
|
||||
* <p>
|
||||
* 原有条件: name=? or type=?
|
||||
* <p>
|
||||
* 执行后条件: (name=? or type=?) and userId=?
|
||||
*
|
||||
* @see this#toNestQuery(Consumer)
|
||||
* @since 3.0.4
|
||||
*/
|
||||
public <T> Query<T, QueryParamEntity> toNestQuery() {
|
||||
return toNestQuery(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将已有的条件包装到一个嵌套的条件里,并返回一个Query对象.例如:
|
||||
* <pre>
|
||||
* entity.toNestQuery(query->query.and("userId",userId));
|
||||
* </pre>
|
||||
* <p>
|
||||
* 原有条件: name=? or type=?
|
||||
* <p>
|
||||
* 执行后条件: userId=? (name=? or type=?)
|
||||
*
|
||||
* @param before 在包装之前执行,将条件包装到已有条件之前
|
||||
* @since 3.0.4
|
||||
*/
|
||||
public <T> Query<T, QueryParamEntity> toNestQuery(Consumer<Query<T, QueryParamEntity>> before) {
|
||||
List<Term> terms = getTerms();
|
||||
setTerms(new ArrayList<>());
|
||||
Query<T, QueryParamEntity> query = toQuery();
|
||||
if (null != before) {
|
||||
before.accept(query);
|
||||
}
|
||||
return query
|
||||
.nest()
|
||||
.each(terms, NestConditional::accept)
|
||||
.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置条件表达式,可以通过表达式的方式快速构建查询条件. 表达式是类似sql条件的语法,如:
|
||||
* <pre>
|
||||
* name is 测试 and age gte 10
|
||||
* </pre>
|
||||
* <pre>
|
||||
* name is 测试 and (age gt 10 or age lte 90 )
|
||||
* </pre>
|
||||
*
|
||||
* @param termExpression 表达式
|
||||
* @see 3.0.5
|
||||
*/
|
||||
public void setTermExpression(String termExpression) {
|
||||
this.termExpression = termExpression;
|
||||
setTerms(TermExpressionParser.parse(termExpression));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Term> getTerms() {
|
||||
List<Term> terms = super.getTerms();
|
||||
if (CollectionUtils.isEmpty(terms) && StringUtils.hasText(termExpression)) {
|
||||
setTerms(terms = TermExpressionParser.parse(termExpression));
|
||||
}
|
||||
return terms;
|
||||
}
|
||||
|
||||
public QueryParamEntity noPaging() {
|
||||
setPaging(false);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
import org.hswebframework.ezorm.core.NestConditional;
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 动态条件表达式解析器
|
||||
* name=测试 and age=test
|
||||
*/
|
||||
public class TermExpressionParser {
|
||||
|
||||
public static List<Term> parse(String expression) {
|
||||
Query<?, QueryParamEntity> conditional = QueryParamEntity.newQuery();
|
||||
|
||||
NestConditional<?> nest = null;
|
||||
|
||||
// 字符容器
|
||||
char[] buf = new char[128];
|
||||
// 记录词项的长度, Arrays.copyOf使用
|
||||
byte len = 0;
|
||||
// 空格数量?
|
||||
byte spaceLen = 0;
|
||||
// 当前列
|
||||
char[] currentColumn = null;
|
||||
// 当前列对应的值
|
||||
char[] currentValue = null;
|
||||
// 当前条件类型 eq btw in ...
|
||||
String currentTermType = null;
|
||||
// 当前链接类型 and / or
|
||||
String currentType = "and";
|
||||
// 是否是引号, 单引号 / 双引号
|
||||
byte quotationMarks = 0;
|
||||
// 表达式字符数组
|
||||
char[] all = expression.toCharArray();
|
||||
|
||||
for (char c : all) {
|
||||
|
||||
if (c == '\'' || c == '"') {
|
||||
if (quotationMarks != 0) {
|
||||
// 碰到(结束的)单/双引号, 标志归零, 跳过
|
||||
quotationMarks = 0;
|
||||
continue;
|
||||
}
|
||||
// 碰到(开始的)单/双引号, 做记录, 跳过
|
||||
quotationMarks++;
|
||||
continue;
|
||||
} else if (c == '(') {
|
||||
nest = (nest == null ?
|
||||
(currentType.equals("or") ? conditional.orNest() : conditional.nest()) :
|
||||
(currentType.equals("or") ? nest.orNest() : nest.nest()));
|
||||
len = 0;
|
||||
continue;
|
||||
} else if (c == ')') {
|
||||
if (nest == null) {
|
||||
continue;
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
currentColumn = null;
|
||||
currentTermType = null;
|
||||
}
|
||||
Object end = nest.end();
|
||||
nest = end instanceof NestConditional ? ((NestConditional) end) : null;
|
||||
len = 0;
|
||||
spaceLen++;
|
||||
continue;
|
||||
} else if (c == '=' || c == '>' || c == '<') {
|
||||
if (currentTermType != null) {
|
||||
currentTermType += String.valueOf(c);
|
||||
//spaceLen--;
|
||||
} else {
|
||||
currentTermType = String.valueOf(c);
|
||||
}
|
||||
|
||||
if (currentColumn == null) {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
}
|
||||
spaceLen++;
|
||||
len = 0;
|
||||
continue;
|
||||
} else if (c == ' ') {
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (quotationMarks != 0) {
|
||||
// 如果当前字符是空格,并且前面迭代时碰到过单/双引号, 不处理并且添加到buf中
|
||||
buf[len++] = c;
|
||||
continue;
|
||||
}
|
||||
spaceLen++;
|
||||
if (currentColumn == null && (spaceLen == 1 || spaceLen % 5 == 0)) {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
continue;
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
if (null == currentTermType) {
|
||||
currentTermType = new String(Arrays.copyOf(buf, len));
|
||||
len = 0;
|
||||
continue;
|
||||
}
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
if (nest != null) {
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
} else {
|
||||
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
}
|
||||
currentColumn = null;
|
||||
currentTermType = null;
|
||||
len = 0;
|
||||
continue;
|
||||
} else if (len == 2 || len == 3) {
|
||||
String type = new String(Arrays.copyOf(buf, len));
|
||||
if (type.equalsIgnoreCase("or")) {
|
||||
currentType = "or";
|
||||
if (nest != null) {
|
||||
nest.or();
|
||||
} else {
|
||||
conditional.or();
|
||||
}
|
||||
len = 0;
|
||||
continue;
|
||||
} else if (type.equalsIgnoreCase("and")) {
|
||||
currentType = "and";
|
||||
if (nest != null) {
|
||||
nest.and();
|
||||
} else {
|
||||
conditional.and();
|
||||
}
|
||||
len = 0;
|
||||
continue;
|
||||
} else {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
spaceLen++;
|
||||
}
|
||||
} else {
|
||||
currentColumn = Arrays.copyOf(buf, len);
|
||||
len = 0;
|
||||
spaceLen++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[len++] = c;
|
||||
}
|
||||
if (null != currentColumn) {
|
||||
currentValue = Arrays.copyOf(buf, len);
|
||||
if (nest != null) {
|
||||
nest.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
} else {
|
||||
conditional.accept(new String(currentColumn), convertTermType(currentTermType), new String(currentValue));
|
||||
}
|
||||
}
|
||||
return conditional.getParam().getTerms();
|
||||
}
|
||||
|
||||
private static String convertTermType(String termType) {
|
||||
if (termType == null) {
|
||||
return TermType.eq;
|
||||
}
|
||||
switch (termType) {
|
||||
case "=":
|
||||
return TermType.eq;
|
||||
case ">":
|
||||
return TermType.gt;
|
||||
case "<":
|
||||
return TermType.lt;
|
||||
case ">=":
|
||||
return TermType.gte;
|
||||
case "<=":
|
||||
return TermType.lte;
|
||||
default:
|
||||
return termType;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.hswebframework.web.crud.service;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class DefaultReactiveCrudService<E,K> implements ReactiveCrudService<E,K> {
|
||||
|
||||
@Autowired
|
||||
private ReactiveRepository<E, K> repository;
|
||||
|
||||
@Override
|
||||
public ReactiveRepository<E, K> getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.hswebframework.web.crud.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
|
||||
import org.hswebframework.web.crud.entity.PagerResult;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public interface ReactiveCrudService<E, K> {
|
||||
|
||||
ReactiveRepository<E, K> getRepository();
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Mono<E> findById(Mono<K> publisher) {
|
||||
return getRepository().findById(publisher);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Flux<E> findById(Flux<K> publisher) {
|
||||
return publisher.flatMap(e -> findById(Mono.just(e)));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default Mono<SaveResult> save(Publisher<E> entityPublisher) {
|
||||
return getRepository()
|
||||
.save(entityPublisher);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default Mono<Integer> deleteById(Publisher<K> idPublisher) {
|
||||
return getRepository()
|
||||
.deleteById(idPublisher);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Flux<E> query(Mono<? extends QueryParam> queryParamMono) {
|
||||
return queryParamMono
|
||||
.flatMapMany(param -> getRepository()
|
||||
.createQuery()
|
||||
.setParam(param)
|
||||
.fetch());
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Mono<PagerResult<E>> queryPager(Mono<? extends QueryParam> queryParamMono) {
|
||||
return count(queryParamMono)
|
||||
.zipWhen(total -> {
|
||||
if (total == 0) {
|
||||
return Mono.just(Collections.<E>emptyList());
|
||||
}
|
||||
return queryParamMono
|
||||
.map(QueryParam::clone)
|
||||
.flatMap(q -> query(Mono.just(q.rePaging(total))).collectList());
|
||||
}, PagerResult::of);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Mono<Integer> count(Mono<? extends QueryParam> queryParamMono) {
|
||||
return queryParamMono
|
||||
.flatMap(param -> getRepository()
|
||||
.createQuery()
|
||||
.setParam(param)
|
||||
.count());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-commons-dao</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
# 基于mybatis的通用crud实现
|
||||
|
||||
使用myabtis和easy-orm对`hsweb-commons-dao-api`进行了实现,提供动态条件对crud支持.
|
||||
|
||||
# 使用
|
||||
在pom.xml中引入:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-dao-mybatis</artifactId>
|
||||
<version>${hsweb.framework.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
# 配置
|
||||
application.yml
|
||||
|
||||
```yaml
|
||||
mybatis:
|
||||
# 扫描myabtis mapper xml的路径
|
||||
mapper-locations: classpath*:com/company/app/**/*Mapper.xml
|
||||
# 这里需要配置扫描枚举,才能支持对实现了EnumDict接口的枚举进行序列化和反序列化
|
||||
type-handlers-package: com.company.app.enums
|
||||
# 是否开启动态数据源,开启后才能支持在同一个dao中切换数据源
|
||||
dynamic-datasource: false
|
||||
# 排除扫描xml配置,用于需要拓展无法修改的mapper xml时,通过此配置不加载对应的xml,然后通过mapper-locations配置加载新的xml.
|
||||
mapper-location-excludes: classpath*:com/company/app/x/y/*Mapper.xml
|
||||
```
|
||||
|
||||
# 使用通用Mapper XMl
|
||||
|
||||
目前仅支持xml的方式,例如:
|
||||
|
||||
```xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="org.hswebframework.web.dao.crud.TestDao">
|
||||
|
||||
<resultMap id="TestResultMap" type="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<!--这里需要声明id-->
|
||||
<id property="id" column="id" javaType="Long" jdbcType="INTEGER"/>
|
||||
<!--如果没有使用jpa注解,需要在这里添加配置,因为动态生成sql的时候是根据resultMap中的字段进行配置的-->
|
||||
</resultMap>
|
||||
|
||||
<!--另外一个resultMap,用于演示表关联-->
|
||||
<resultMap id="TestNestResultMap" type="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<id property="id" column="id" javaType="Long" jdbcType="INTEGER"/>
|
||||
<!--jpa目前不支持表关联的解析,所以要在这里定义另外一个关联实体的全部信息,其中,column=关联表别名.列名-->
|
||||
<result property="nest.name" column="nest_table.name" javaType="String" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<!--用于动态生成sql所需的配置-->
|
||||
<sql id="config">
|
||||
<!--声明2个在生成动态sql时需要用到的变量-->
|
||||
<!--注意value里的单引号,因为value属性为一个ognl表达式-->
|
||||
<bind name="resultMapId" value="'TestResultMap'"/>
|
||||
<bind name="tableName" value="'h_test'"/>
|
||||
</sql>
|
||||
|
||||
<!--注意:keyColumn,keyProperty,useGeneratedKeys,只有在数据库生成id的时候才需要配置-->
|
||||
<insert id="insert" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildInsertSql"/>
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="org.hswebframework.web.commons.entity.Entity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildUpdateSql"/>
|
||||
</update>
|
||||
|
||||
<select id="query" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="TestResultMap">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildSelectSql"/>
|
||||
</select>
|
||||
|
||||
<select id="count" parameterType="org.hswebframework.web.commons.entity.Entity" resultType="int">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildTotalSql"/>
|
||||
</select>
|
||||
|
||||
<delete id="delete" parameterType="org.hswebframework.web.commons.entity.Entity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildDeleteSql"/>
|
||||
</delete>
|
||||
|
||||
<!--表关联的查询-->
|
||||
<select id="queryNest" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="TestNestResultMap">
|
||||
<bind name="tableName" value="'h_test'"/>
|
||||
<bind name="resultMapId" value="'TestNestResultMap'"/>
|
||||
select
|
||||
<include refid="BasicMapper.buildSelectField"/>
|
||||
from h_test <!--注意h_nest_table的别名:nest_table-->
|
||||
left join h_nest_table nest_table on nest_table.id=h_test.id
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
<include refid="BasicMapper.buildSortField"/>
|
||||
</select>
|
||||
|
||||
<select id="countNest" parameterType="org.hswebframework.web.commons.entity.Entity" resultType="int">
|
||||
<include refid="config"/>
|
||||
select
|
||||
count(1)
|
||||
from h_test
|
||||
left join h_nest_table nest_table on nest_table.id=h_test.id
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
```
|
||||
|
||||
⚠️注意:query(count),update,delete方法的参数目前仅实现了:
|
||||
|
||||
`org.hswebframework.web.commons.entity.param.QueryParamEntity`
|
||||
|
||||
`org.hswebframework.web.commons.entity.param.UpdateParamEntity`
|
||||
|
||||
`org.hswebframework.web.commons.entity.param.DeleteParamEntity`
|
||||
|
||||
因此在实际调用都时候,目前只能接收对应以上参数.
|
||||
但是在接口中仍然使用`org.hswebframework.web.commons.entity.Entity`作为参数,用于预留给以后提供更多都参数实现.
|
||||
|
||||
# 动态条件
|
||||
此模块使用hsweb-easyorm项目来进行动态SQL条件的生成,主要是通过:在上述的`**ParamEntity`类中的属性 `List<Term> terms;`
|
||||
进行处理,`Term`为一个SQL条件,例如一个简单的嵌套条件:
|
||||
```text
|
||||
[{
|
||||
column:"name",
|
||||
termType:"eq", #SQL条件类型 =
|
||||
value:"张三",
|
||||
terms:[
|
||||
{
|
||||
column:"address",
|
||||
termType:"like", //SQL条件类型 like
|
||||
value:"北京%"
|
||||
},
|
||||
{
|
||||
column:"address",
|
||||
type:"or" //和前面的条件成or关系,如果不指定,默认为and
|
||||
termType:"like", //SQL条件类型 like
|
||||
value:"上海%"
|
||||
}
|
||||
]
|
||||
}]
|
||||
```
|
||||
对应的sql条件为: where name = ? and (address like ? or address lke ?)
|
||||
|
||||
条件构造方式看上去过于复杂? 可以使用DSL方式的构建工具类:`org.hswebframework.ezorm.core.dsl.Query`来进行构建.
|
||||
在[hsweb-commons-service-api](../../hsweb-commons-service/hsweb-commons-service-api)模块也会提供便捷的条件创建方式.
|
||||
|
||||
如果参数来自客户端请求,可封装一个通用的js进行构建, 你可以在前端放心的构建参数,所有的条件都使用参数化预编译的方式拼接SQL,不存在SQL注入问题.
|
||||
|
||||
## 默认支持的动态条件列表
|
||||
| termType | 对应SQL | 说明 |
|
||||
| ------------- |:-------------:| ----|
|
||||
|eq|=?| 等于|
|
||||
|not |!=?| 不等于|
|
||||
|gt|>?| 大于|
|
||||
|gte|>= ?| 大于等于|
|
||||
|lt|< ?| 小于|
|
||||
|lte| <= ?| 小于等于|
|
||||
|like|like ?| 模糊匹配,如果需要统配符,请自行拼接value,如: value+"%" |
|
||||
|nlike|not like ?| 同like |
|
||||
|in|in(?,?)| value 可使用半角逗号(,)分隔,或者数组或者`Collection`接口的实现 |
|
||||
|nin|not in(?,?)| value 可使用半角逗号(,)分隔,或者数组或者`Collection`接口的实现 |
|
||||
|isnull| is null | value为任意不为空的值即可 |
|
||||
|notnull| not null | value为任意不为空的值即可 |
|
||||
|empty| ='' | value为任意不为空的值即可 |
|
||||
|nempty| !='' | value为任意不为空的值即可 |
|
||||
|bwt|between ? and ? | value 可使用半角逗号(,)分隔,或者使用数组或者Collection接口的实现|
|
||||
|nbwt|not between ? and ? | value 可使用半角逗号(,)分隔,或者使用数组或者Collection接口的实现|
|
||||
|
||||
## 拓展动态条件
|
||||
在某些需要自定义查询条件的场景,比如关联条件,可通过实现`SqlTermCustomer`接口并注入到spring来进行自定义SQL条件的拼接,
|
||||
例如:
|
||||
```java
|
||||
//AbstractSqlTermCustomer提供了一些便利的方法
|
||||
@org.springframework.stereotype.Component
|
||||
public class MyTerm extends AbstractSqlTermCustomer{
|
||||
|
||||
@Override
|
||||
public String getTermType() {
|
||||
//对应Term参数中的属性termType
|
||||
return "my-term";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialect[] forDialect() {
|
||||
//对特定对数据库类型生效,返回null时对全部支持对数据库类型生效
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SqlAppender accept(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
//当传入了my-term条件对时候,会调用此方法进行拼接
|
||||
//对传入对参数进行转换,此步骤为必须的
|
||||
ChangedTermValue termValue =createChangedValue(term);
|
||||
|
||||
//转换参数,将参数转为集合,以支持in查询.
|
||||
List<Object> idList = BoostTermTypeMapper.convertList(column, termValue.getOld());
|
||||
|
||||
SqlAppender appender= new SqlAppender();
|
||||
|
||||
appender.add(createColumnName(column,tableAlias),"in (select id from my_table where t_id");
|
||||
|
||||
//根据参数的数量,构造对应的=或者in条件
|
||||
Object newValue= appendCondition(idList,wherePrefix,appender);
|
||||
|
||||
appender.add(")");
|
||||
//设置新的值到条件中
|
||||
termValue.setValue(newValue);
|
||||
|
||||
return appender;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用: 在查询的时候,将`Term`的`termType`属性值设置为`my-term`即可.
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ /*
|
||||
~ * Copyright 2019 http://www.hswebframework.org
|
||||
~ *
|
||||
~ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ * you may not use this file except in compliance with the License.
|
||||
~ * You may obtain a copy of the License at
|
||||
~ *
|
||||
~ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ *
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ * See the License for the specific language governing permissions and
|
||||
~ * limitations under the License.
|
||||
~ */
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons-dao</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-commons-dao-mybatis</artifactId>
|
||||
|
||||
<description>通用增删改查-通用Dao的mybatis实现</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-dao-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-datasource-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
<version>1.0.0.Final</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.0.26</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.springframework.boot</groupId>-->
|
||||
<!--<artifactId>spring-boot-starter-data-jpa</artifactId>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,158 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.apache.ibatis.type.TypeHandlerRegistry;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
import org.hswebframework.web.dict.defaults.DefaultDictDefineRepository;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.util.StringUtils.tokenizeToStringArray;
|
||||
|
||||
@Slf4j
|
||||
public class EnumDictHandlerRegister {
|
||||
|
||||
static TypeHandlerRegistry typeHandlerRegistry;
|
||||
|
||||
private static MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
|
||||
|
||||
private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
|
||||
public static void register(String packages) {
|
||||
register(tokenizeToStringArray(packages,
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void register(String[] packages) {
|
||||
if (typeHandlerRegistry == null) {
|
||||
log.error("请在spring容器初始化后再调用此方法!");
|
||||
return;
|
||||
}
|
||||
for (String basePackage : packages) {
|
||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
ClassUtils.convertClassNameToResourcePath(basePackage) + "/**/*.class";
|
||||
try {
|
||||
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
|
||||
for (Resource resource : resources) {
|
||||
try {
|
||||
MetadataReader reader = metadataReaderFactory.getMetadataReader(resource);
|
||||
Class enumType = Class.forName(reader.getClassMetadata().getClassName());
|
||||
if (enumType.isEnum() && EnumDict.class.isAssignableFrom(enumType)) {
|
||||
log.debug("register enum dict:{}", enumType);
|
||||
DefaultDictDefineRepository.registerDefine(DefaultDictDefineRepository.parseEnumDict(enumType));
|
||||
//注册枚举类型
|
||||
typeHandlerRegistry.register(enumType, new EnumDictHandler(enumType));
|
||||
|
||||
//注册枚举数组类型
|
||||
typeHandlerRegistry.register(Array.newInstance(enumType, 0).getClass(), new EnumDictArrayHandler(enumType));
|
||||
}
|
||||
} catch (Exception | Error ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("register enum dict error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@MappedJdbcTypes({JdbcType.NUMERIC, JdbcType.TINYINT, JdbcType.INTEGER, JdbcType.BIGINT})
|
||||
static class EnumDictArrayHandler<T extends Enum & EnumDict> implements TypeHandler<Object[]> {
|
||||
|
||||
private Class<T> type;
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, Object[] parameter, JdbcType jdbcType) throws SQLException {
|
||||
T[] ts = ((T[]) parameter);
|
||||
ps.setLong(i, EnumDict.toMask(ts));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return toArray(rs.getLong(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return toArray(rs.getLong(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return toArray(cs.getLong(columnIndex));
|
||||
}
|
||||
|
||||
private Object[] toArray(Long value) {
|
||||
if (null == value) {
|
||||
return null;
|
||||
}
|
||||
List<T> ts = EnumDict.getByMask(getType(), value);
|
||||
return ts.toArray((Object[]) Array.newInstance(type, ts.size()));
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.BIT,
|
||||
JdbcType.BOOLEAN, JdbcType.NUMERIC,
|
||||
JdbcType.TINYINT, JdbcType.INTEGER,
|
||||
JdbcType.BIGINT, JdbcType.DECIMAL,
|
||||
JdbcType.CHAR})
|
||||
static class EnumDictHandler<T extends Enum & EnumDict> implements TypeHandler<T> {
|
||||
|
||||
private Class<T> type;
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
|
||||
if (parameter == null) {
|
||||
ps.setNull(i, jdbcType.TYPE_CODE);
|
||||
} else {
|
||||
ps.setObject(i, parameter.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
Object val = rs.getObject(columnName);
|
||||
return EnumDict.findByValue(getType(), val).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
Object val = rs.getObject(columnIndex);
|
||||
return EnumDict.findByValue(getType(), val).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
Object val = cs.getObject(columnIndex);
|
||||
return EnumDict.findByValue(getType(), val).orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.session.TransactionIsolationLevel;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.hswebframework.web.commons.entity.factory.EntityFactory;
|
||||
import org.hswebframework.web.dao.mybatis.builder.EasyOrmSqlBuilder;
|
||||
import org.hswebframework.web.dao.mybatis.dynamic.DynamicDataSourceSqlSessionFactoryBuilder;
|
||||
import org.hswebframework.web.dao.mybatis.dynamic.DynamicSpringManagedTransaction;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
||||
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(MybatisProperties.class)
|
||||
public class MyBatisAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Interceptor[] interceptors;
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
@Autowired(required = false)
|
||||
private DatabaseIdProvider databaseIdProvider;
|
||||
|
||||
@Autowired(required = false)
|
||||
private EntityFactory entityFactory;
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
|
||||
public MybatisProperties mybatisProperties() {
|
||||
return new MybatisProperties();
|
||||
}
|
||||
|
||||
@Bean(name = "sqlSessionFactory")
|
||||
@Primary
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
||||
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
|
||||
MybatisProperties mybatisProperties = this.mybatisProperties();
|
||||
if (null != entityFactory) {
|
||||
factory.setObjectFactory(new MybatisEntityFactory(entityFactory));
|
||||
}
|
||||
factory.setVfs(SpringBootVFS.class);
|
||||
if (mybatisProperties().isDynamicDatasource()) {
|
||||
factory.setSqlSessionFactoryBuilder(new DynamicDataSourceSqlSessionFactoryBuilder());
|
||||
factory.setTransactionFactory(new SpringManagedTransactionFactory() {
|
||||
@Override
|
||||
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
|
||||
return new DynamicSpringManagedTransaction();
|
||||
}
|
||||
});
|
||||
}
|
||||
factory.setDataSource(dataSource);
|
||||
if (StringUtils.hasText(mybatisProperties.getConfigLocation())) {
|
||||
factory.setConfigLocation(this.resourceLoader.getResource(mybatisProperties
|
||||
.getConfigLocation()));
|
||||
}
|
||||
if (mybatisProperties.getConfiguration() != null) {
|
||||
factory.setConfiguration(mybatisProperties.getConfiguration());
|
||||
}
|
||||
if (this.interceptors != null && this.interceptors.length > 0) {
|
||||
factory.setPlugins(this.interceptors);
|
||||
}
|
||||
if (this.databaseIdProvider != null) {
|
||||
factory.setDatabaseIdProvider(this.databaseIdProvider);
|
||||
}
|
||||
factory.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
|
||||
String typeHandlers = "org.hswebframework.web.dao.mybatis.handler";
|
||||
if (mybatisProperties.getTypeHandlersPackage() != null) {
|
||||
typeHandlers = typeHandlers + ";" + mybatisProperties.getTypeHandlersPackage();
|
||||
}
|
||||
factory.setTypeHandlersPackage(typeHandlers);
|
||||
factory.setMapperLocations(mybatisProperties.resolveMapperLocations());
|
||||
|
||||
SqlSessionFactory sqlSessionFactory = factory.getObject();
|
||||
MybatisUtils.sqlSession = sqlSessionFactory;
|
||||
|
||||
EnumDictHandlerRegister.typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
|
||||
EnumDictHandlerRegister.register("org.hswebframework.web;" + mybatisProperties.getTypeHandlersPackage());
|
||||
|
||||
try {
|
||||
Class.forName("javax.persistence.Table");
|
||||
EasyOrmSqlBuilder.getInstance().useJpa = mybatisProperties.isUseJpa();
|
||||
} catch (@SuppressWarnings("all") Exception ignore) {
|
||||
}
|
||||
EasyOrmSqlBuilder.getInstance().entityFactory = entityFactory;
|
||||
|
||||
return sqlSessionFactory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.hswebframework.web.dao.Dao;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.SqlTermCustomizer;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.dict.DictInTermTypeMapper;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.dict.DictTermTypeMapper;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.hswebframework.web.dao.mybatis")
|
||||
@MapperScan(value = "org.hswebframework.web.dao"
|
||||
, markerInterface = Dao.class
|
||||
, sqlSessionFactoryRef = "sqlSessionFactory")
|
||||
@AutoConfigureAfter(MyBatisAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(MybatisProperties.class)
|
||||
public class MybatisDaoAutoConfiguration {
|
||||
@Bean
|
||||
public DictTermTypeMapper dictTermTypeMapper() {
|
||||
return new DictTermTypeMapper(false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DictTermTypeMapper dictNotTermTypeMapper() {
|
||||
return new DictTermTypeMapper(true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DictInTermTypeMapper dictInTermTypeMapper() {
|
||||
return new DictInTermTypeMapper(false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DictInTermTypeMapper dictNotInTermTypeMapper() {
|
||||
return new DictInTermTypeMapper(true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor sqlTermCustomizerRegister() {
|
||||
|
||||
List<Dialect> dialects = Arrays.asList(
|
||||
Dialect.H2
|
||||
, Dialect.MYSQL
|
||||
, Dialect.ORACLE
|
||||
, Dialect.POSTGRES
|
||||
, Dialect.MSSQL);
|
||||
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof SqlTermCustomizer) {
|
||||
SqlTermCustomizer customizer = ((SqlTermCustomizer) bean);
|
||||
if (customizer.forDialect() != null) {
|
||||
for (Dialect dialect : customizer.forDialect()) {
|
||||
dialect.setTermTypeMapper(customizer.getTermType(), customizer);
|
||||
}
|
||||
} else {
|
||||
dialects.forEach(dialect -> dialect.setTermTypeMapper(customizer.getTermType(), customizer));
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
|
||||
import org.hswebframework.web.commons.entity.factory.EntityFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 使用EntityFactory来拓展mybatis实体
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class MybatisEntityFactory extends DefaultObjectFactory {
|
||||
|
||||
private static final long serialVersionUID = -7388760632000329910L;
|
||||
|
||||
private transient EntityFactory entityFactory;
|
||||
|
||||
public MybatisEntityFactory(EntityFactory entityFactory) {
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveInterface(Class<?> type) {
|
||||
Class<?> classToCreate;
|
||||
if (type == List.class || type == Collection.class || type == Iterable.class) {
|
||||
classToCreate = ArrayList.class;
|
||||
} else if (type == Map.class) {
|
||||
classToCreate = HashMap.class;
|
||||
} else if (type == SortedSet.class) { // issue #510 Collections Support
|
||||
classToCreate = TreeSet.class;
|
||||
} else if (type == Set.class) {
|
||||
classToCreate = HashSet.class;
|
||||
} else {
|
||||
// entity interface
|
||||
classToCreate = entityFactory.getInstanceType(type);
|
||||
}
|
||||
return classToCreate;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
/**
|
||||
* 排除不需要加载的mapper.xml
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface MybatisMapperCustomizer {
|
||||
String[] getExcludes();
|
||||
|
||||
String[] getIncludes();
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* mybatis配置,继承官方配置类,增加一些属性以拓展更多功能
|
||||
* <ul>
|
||||
* <li>是否启用动态数据源{@link this#dynamicDatasource}</li>
|
||||
* <li>可设置不加载的配置{@link this#mapperLocationExcludes}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see org.mybatis.spring.boot.autoconfigure.MybatisProperties
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MybatisProperties extends org.mybatis.spring.boot.autoconfigure.MybatisProperties {
|
||||
/**
|
||||
* 默认支持的hsweb mapper
|
||||
*/
|
||||
private static final String defaultMapperLocation = "classpath*:org/hswebframework/web/dao/mybatis/mappers/**/*.xml";
|
||||
/**
|
||||
* 是否启用动态数据源
|
||||
* 启用后调用{@link DataSourceHolder#switcher()},mybatis也会进行数据源切换
|
||||
*
|
||||
* @see DataSourceHolder#switcher()
|
||||
*/
|
||||
private boolean dynamicDatasource = false;
|
||||
/**
|
||||
* 排除加载的mapper.xml
|
||||
* 想自定义mapper并覆盖原始mapper的场景下,通过设置此属性来排除配置文件。
|
||||
* 排除使用{@link Resource#getURL()#toString()}进行对比
|
||||
*/
|
||||
private String[] mapperLocationExcludes = null;
|
||||
/**
|
||||
* 使用jpa注解来解析表结构,动态生成查询条件
|
||||
*/
|
||||
private boolean useJpa = true;
|
||||
|
||||
private List<MybatisMapperCustomizer> mybatisMappers;
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setMybatisMappers(List<MybatisMapperCustomizer> mybatisMappers) {
|
||||
this.mybatisMappers = mybatisMappers;
|
||||
}
|
||||
|
||||
public String[] getMapperLocationExcludes() {
|
||||
return mapperLocationExcludes;
|
||||
}
|
||||
|
||||
public void setMapperLocationExcludes(String[] mapperLocationExcludes) {
|
||||
this.mapperLocationExcludes = mapperLocationExcludes;
|
||||
}
|
||||
|
||||
public boolean isDynamicDatasource() {
|
||||
return dynamicDatasource;
|
||||
}
|
||||
|
||||
public void setDynamicDatasource(boolean dynamicDatasource) {
|
||||
this.dynamicDatasource = dynamicDatasource;
|
||||
}
|
||||
|
||||
public void setUseJpa(boolean useJpa) {
|
||||
this.useJpa = useJpa;
|
||||
}
|
||||
|
||||
public boolean isUseJpa() {
|
||||
return useJpa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource[] resolveMapperLocations() {
|
||||
Map<String, Resource> resources = new HashMap<>();
|
||||
Set<String> locations;
|
||||
|
||||
if (this.getMapperLocations() == null) {
|
||||
locations = new HashSet<>();
|
||||
} else {
|
||||
locations = Arrays.stream(getMapperLocations()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
locations.add(defaultMapperLocation);
|
||||
|
||||
if (mybatisMappers != null) {
|
||||
mybatisMappers.stream()
|
||||
.map(MybatisMapperCustomizer::getIncludes)
|
||||
.flatMap(Arrays::stream)
|
||||
.forEach(locations::add);
|
||||
}
|
||||
|
||||
for (String mapperLocation : locations) {
|
||||
Resource[] mappers;
|
||||
try {
|
||||
mappers = new PathMatchingResourcePatternResolver().getResources(mapperLocation);
|
||||
for (Resource mapper : mappers) {
|
||||
resources.put(mapper.getURL().toString(), mapper);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
Set<String> excludes = new HashSet<>();
|
||||
if (mybatisMappers != null) {
|
||||
mybatisMappers.stream()
|
||||
.map(MybatisMapperCustomizer::getExcludes)
|
||||
.flatMap(Arrays::stream)
|
||||
.forEach(excludes::add);
|
||||
}
|
||||
if (mapperLocationExcludes != null && mapperLocationExcludes.length > 0) {
|
||||
for (String exclude : mapperLocationExcludes) {
|
||||
excludes.add(exclude);
|
||||
}
|
||||
}
|
||||
PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
//排除不需要的配置
|
||||
for (String mapperLocationExclude : excludes) {
|
||||
try {
|
||||
Resource[] excludesMappers = resourcePatternResolver.getResources(mapperLocationExclude);
|
||||
for (Resource excludesMapper : excludesMappers) {
|
||||
resources.remove(excludesMapper.getURL().toString());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
Resource[] mapperLocations = new Resource[resources.size()];
|
||||
mapperLocations = resources.values().toArray(mapperLocations);
|
||||
return mapperLocations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis;
|
||||
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MybatisUtils {
|
||||
volatile static SqlSessionFactory sqlSession;
|
||||
|
||||
public static ResultMap getResultMap(String id) {
|
||||
return getSqlSession().getConfiguration().getResultMap(id);
|
||||
}
|
||||
|
||||
public static SqlSessionFactory getSqlSession() {
|
||||
if (sqlSession == null) {
|
||||
throw new UnsupportedOperationException("sqlSession is null");
|
||||
}
|
||||
return sqlSession;
|
||||
}
|
||||
}
|
||||
@@ -1,546 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
import org.apache.commons.beanutils.PropertyUtilsBean;
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.mapping.ResultMapping;
|
||||
import org.hswebframework.ezorm.core.ValueConverter;
|
||||
import org.hswebframework.ezorm.core.param.*;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBDatabaseMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBTableMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.BooleanValueConverter;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.DateTimeConverter;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.NumberValueConverter;
|
||||
import org.hswebframework.ezorm.rdb.render.Sql;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlRender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.*;
|
||||
import org.hswebframework.ezorm.rdb.render.support.simple.CommonSqlRender;
|
||||
import org.hswebframework.ezorm.rdb.render.support.simple.SimpleWhereSqlBuilder;
|
||||
import org.hswebframework.web.BusinessException;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.factory.EntityFactory;
|
||||
import org.hswebframework.web.dao.mybatis.builder.jpa.JpaAnnotationParser;
|
||||
import org.hswebframework.web.dao.mybatis.handler.NumberBooleanTypeHandler;
|
||||
import org.hswebframework.web.dao.mybatis.plgins.pager.Pager;
|
||||
import org.hswebframework.web.dao.mybatis.MybatisUtils;
|
||||
import org.hswebframework.utils.StringUtils;
|
||||
import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
import org.hswebframework.web.datasource.DatabaseType;
|
||||
|
||||
import java.sql.JDBCType;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
|
||||
/**
|
||||
* 使用easyorm 动态构建 sql
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 2.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class EasyOrmSqlBuilder {
|
||||
|
||||
public volatile boolean useJpa = false;
|
||||
|
||||
public EntityFactory entityFactory;
|
||||
|
||||
private static final EasyOrmSqlBuilder instance = new EasyOrmSqlBuilder();
|
||||
protected static final Map<Class, String> simpleName = new HashMap<>();
|
||||
|
||||
protected PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils();
|
||||
|
||||
public static EasyOrmSqlBuilder getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private EasyOrmSqlBuilder() {
|
||||
}
|
||||
|
||||
static {
|
||||
simpleName.put(Integer.class, "int");
|
||||
simpleName.put(Byte.class, "byte");
|
||||
simpleName.put(Double.class, "double");
|
||||
simpleName.put(Float.class, "float");
|
||||
simpleName.put(Boolean.class, "boolean");
|
||||
simpleName.put(Long.class, "long");
|
||||
simpleName.put(Short.class, "short");
|
||||
simpleName.put(Character.class, "char");
|
||||
simpleName.put(String.class, "string");
|
||||
simpleName.put(int.class, "int");
|
||||
simpleName.put(double.class, "double");
|
||||
simpleName.put(float.class, "float");
|
||||
simpleName.put(boolean.class, "boolean");
|
||||
simpleName.put(long.class, "long");
|
||||
simpleName.put(short.class, "short");
|
||||
simpleName.put(char.class, "char");
|
||||
simpleName.put(byte.class, "byte");
|
||||
}
|
||||
|
||||
public static String getJavaType(Class type) {
|
||||
String javaType = simpleName.get(type);
|
||||
if (javaType == null) {
|
||||
javaType = type.getName();
|
||||
}
|
||||
return javaType;
|
||||
}
|
||||
|
||||
public static final RDBDatabaseMetaData mysql = new MysqlMeta();
|
||||
public static final RDBDatabaseMetaData oracle = new OracleMeta();
|
||||
public static final RDBDatabaseMetaData h2 = new H2Meta();
|
||||
public static final RDBDatabaseMetaData postgresql = new PGMeta();
|
||||
public static final RDBDatabaseMetaData mssql = new MSSQLMeta();
|
||||
|
||||
private final ConcurrentMap<RDBDatabaseMetaData, Map<String, RDBTableMetaData>> metaCache = new ConcurrentHashMap<>();
|
||||
|
||||
public RDBDatabaseMetaData getActiveDatabase() {
|
||||
DatabaseType type = DataSourceHolder.currentDatabaseType();
|
||||
switch (type) {
|
||||
case mysql:
|
||||
return mysql;
|
||||
case oracle:
|
||||
return oracle;
|
||||
case postgresql:
|
||||
return postgresql;
|
||||
case h2:
|
||||
return h2;
|
||||
case jtds_sqlserver:
|
||||
case sqlserver:
|
||||
return mssql;
|
||||
default:
|
||||
log.warn("不支持的数据库类型:[{}]", type);
|
||||
return h2;
|
||||
}
|
||||
}
|
||||
|
||||
private String getRealTableName(String tableName) {
|
||||
|
||||
String newTable = DataSourceHolder.tableSwitcher().getTable(tableName);
|
||||
|
||||
if (!tableName.equals(newTable)) {
|
||||
log.debug("use new table [{}] for [{}]", newTable, tableName);
|
||||
}
|
||||
return newTable;
|
||||
|
||||
}
|
||||
|
||||
private List<RDBColumnMetaData> createColumn(String prefix, String columnName, ResultMapping resultMapping) {
|
||||
List<RDBColumnMetaData> metaData = new ArrayList<>();
|
||||
if (resultMapping.getNestedQueryId() == null) {
|
||||
|
||||
if (resultMapping.getNestedResultMapId() != null) {
|
||||
ResultMap nests = MybatisUtils.getResultMap(resultMapping.getNestedResultMapId());
|
||||
Set<ResultMapping> resultMappings = new HashSet<>(nests.getResultMappings());
|
||||
resultMappings.addAll(nests.getIdResultMappings());
|
||||
for (ResultMapping mapping : resultMappings) {
|
||||
metaData.addAll(createColumn(resultMapping.getProperty(),
|
||||
org.springframework.util.StringUtils.hasText(resultMapping.getColumn())
|
||||
? resultMapping.getColumn()
|
||||
: resultMapping.getProperty(),
|
||||
mapping));
|
||||
}
|
||||
return metaData;
|
||||
}
|
||||
|
||||
JDBCType jdbcType = JDBCType.VARCHAR;
|
||||
try {
|
||||
jdbcType = JDBCType.valueOf(resultMapping.getJdbcType().name());
|
||||
} catch (Exception e) {
|
||||
log.warn("can not parse jdbcType:{}", resultMapping.getJdbcType());
|
||||
}
|
||||
RDBColumnMetaData column = new RDBColumnMetaData();
|
||||
column.setJdbcType(jdbcType);
|
||||
column.setName(org.springframework.util.StringUtils.hasText(columnName)
|
||||
? columnName.concat(".").concat(resultMapping.getColumn()) : resultMapping.getColumn());
|
||||
|
||||
if (resultMapping.getTypeHandler() != null) {
|
||||
column.setProperty("typeHandler", resultMapping.getTypeHandler().getClass().getName());
|
||||
}
|
||||
if (!StringUtils.isNullOrEmpty(resultMapping.getProperty())) {
|
||||
column.setAlias(org.springframework.util.StringUtils.hasText(prefix)
|
||||
? prefix.concat(".").concat(resultMapping.getProperty()) : resultMapping.getProperty());
|
||||
|
||||
}
|
||||
column.setJavaType(resultMapping.getJavaType());
|
||||
column.setProperty("resultMapping", resultMapping);
|
||||
metaData.add(column);
|
||||
}
|
||||
return metaData;
|
||||
}
|
||||
|
||||
protected RDBTableMetaData createMeta(String tableName, String resultMapId) {
|
||||
// tableName = getRealTableName(tableName);
|
||||
RDBDatabaseMetaData active = getActiveDatabase();
|
||||
String cacheKey = tableName.concat("-").concat(resultMapId);
|
||||
Map<String, RDBTableMetaData> cache = metaCache.computeIfAbsent(active, k -> new ConcurrentHashMap<>());
|
||||
|
||||
RDBTableMetaData cached = cache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
RDBTableMetaData rdbTableMetaData = new RDBTableMetaData() {
|
||||
@Override
|
||||
public String getName() {
|
||||
//动态切换表名
|
||||
return getRealTableName(tableName);
|
||||
}
|
||||
};
|
||||
ResultMap resultMaps = MybatisUtils.getResultMap(resultMapId);
|
||||
rdbTableMetaData.setName(tableName);
|
||||
rdbTableMetaData.setDatabaseMetaData(active);
|
||||
|
||||
List<ResultMapping> resultMappings = new ArrayList<>(resultMaps.getResultMappings());
|
||||
resultMappings.addAll(resultMaps.getIdResultMappings());
|
||||
|
||||
resultMappings.stream()
|
||||
.map(mapping -> this.createColumn(null, null, mapping))
|
||||
.flatMap(Collection::stream)
|
||||
.forEach(rdbTableMetaData::addColumn);
|
||||
|
||||
if (useJpa) {
|
||||
Class type = entityFactory == null ? resultMaps.getType() : entityFactory.getInstanceType(resultMaps.getType());
|
||||
RDBTableMetaData parseResult = JpaAnnotationParser.parseMetaDataFromEntity(type);
|
||||
if (parseResult != null) {
|
||||
for (RDBColumnMetaData columnMetaData : parseResult.getColumns()) {
|
||||
if (rdbTableMetaData.findColumn(columnMetaData.getName()) == null) {
|
||||
columnMetaData = columnMetaData.clone();
|
||||
columnMetaData.setProperty("fromJpa", true);
|
||||
rdbTableMetaData.addColumn(columnMetaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (RDBColumnMetaData column : rdbTableMetaData.getColumns()) {
|
||||
//时间
|
||||
if (column.getJdbcType() == JDBCType.DATE || column.getJdbcType() == JDBCType.TIMESTAMP) {
|
||||
ValueConverter dateConvert = new DateTimeConverter("yyyy-MM-dd HH:mm:ss", column.getJavaType()) {
|
||||
@Override
|
||||
public Object getData(Object value) {
|
||||
if (value instanceof Number) {
|
||||
return new Date(((Number) value).longValue());
|
||||
}
|
||||
return super.getData(value);
|
||||
}
|
||||
};
|
||||
column.setValueConverter(dateConvert);
|
||||
} else if (column.getJavaType() == boolean.class || column.getJavaType() == Boolean.class) {
|
||||
column.setValueConverter(new BooleanValueConverter(column.getJdbcType()));
|
||||
column.setProperty("typeHandler", NumberBooleanTypeHandler.class.getName());
|
||||
} else if (TypeUtils.isNumberType(column)) { //数字
|
||||
//数字
|
||||
column.setValueConverter(new NumberValueConverter(column.getJavaType()));
|
||||
}
|
||||
}
|
||||
cache.put(cacheKey, rdbTableMetaData);
|
||||
return rdbTableMetaData;
|
||||
}
|
||||
|
||||
public String buildUpdateFields(String resultMapId, String tableName, UpdateParam param) {
|
||||
Pager.reset();
|
||||
param.excludes("id");
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
RDBDatabaseMetaData databaseMetaDate = getActiveDatabase();
|
||||
Dialect dialect = databaseMetaDate.getDialect();
|
||||
CommonSqlRender render = (CommonSqlRender) databaseMetaDate.getRenderer(SqlRender.TYPE.SELECT);
|
||||
List<CommonSqlRender.OperationColumn> columns = render.parseOperationField(tableMetaData, param);
|
||||
SqlAppender appender = new SqlAppender();
|
||||
Object data = param.getData();
|
||||
Map<String, Object> mapData = FastBeanCopier.copy(data, HashMap::new);
|
||||
|
||||
columns.forEach(column -> {
|
||||
|
||||
RDBColumnMetaData columnMetaData = column.getRDBColumnMetaData();
|
||||
if (columnMetaData == null) {
|
||||
return;
|
||||
}
|
||||
if (columnMetaData.getName().contains(".")) {
|
||||
return;
|
||||
}
|
||||
if (columnMetaData.getProperty("read-only").isTrue()) {
|
||||
return;
|
||||
}
|
||||
Object value = mapData.get(columnMetaData.getAlias());
|
||||
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Sql) {
|
||||
appender.add(",", encodeColumn(dialect, columnMetaData.getName()), "=", ((Sql) value).getSql());
|
||||
} else {
|
||||
value = columnMetaData.getValueConverter().getData(value);
|
||||
|
||||
if (columnMetaData.getOptionConverter() != null) {
|
||||
value = columnMetaData.getOptionConverter().converterData(value);
|
||||
}
|
||||
|
||||
mapData.put(columnMetaData.getAlias(), value);
|
||||
|
||||
String typeHandler = columnMetaData.getProperty("typeHandler").getValue();
|
||||
|
||||
appender.add(",", encodeColumn(dialect, columnMetaData.getName())
|
||||
, "=", "#{data.", columnMetaData.getAlias(),
|
||||
",javaType=", EasyOrmSqlBuilder.getJavaType(columnMetaData.getJavaType()),
|
||||
",jdbcType=", columnMetaData.getJdbcType(),
|
||||
typeHandler != null ? ",typeHandler=" + typeHandler : "",
|
||||
"}");
|
||||
}
|
||||
});
|
||||
if (!appender.isEmpty()) {
|
||||
appender.removeFirst();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("没有列被修改");
|
||||
}
|
||||
return appender.toString();
|
||||
}
|
||||
|
||||
public String encodeColumn(Dialect dialect, String field) {
|
||||
if (field.contains(".")) {
|
||||
String[] tmp = field.split("[.]");
|
||||
return tmp[0] + "." + dialect.getQuoteStart() + (dialect.columnToUpperCase() ? (tmp[1].toUpperCase()) : tmp[1]) + dialect.getQuoteEnd();
|
||||
} else {
|
||||
return dialect.getQuoteStart() + (dialect.columnToUpperCase() ? (field.toUpperCase()) : field) + dialect.getQuoteEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public String buildInsertSql(String resultMapId, String tableName, Object param) {
|
||||
Pager.reset();
|
||||
InsertParam insertParam;
|
||||
if (param instanceof InsertParam) {
|
||||
insertParam = ((InsertParam) param);
|
||||
} else {
|
||||
insertParam = new InsertParam<>(param);
|
||||
}
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
SqlRender<InsertParam> render = tableMetaData.getDatabaseMetaData().getRenderer(SqlRender.TYPE.INSERT);
|
||||
return render.render(tableMetaData, insertParam).getSql();
|
||||
}
|
||||
|
||||
public String buildUpdateSql(String resultMapId, String tableName, UpdateParam param) {
|
||||
Pager.reset();
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
SqlRender<UpdateParam> render = tableMetaData.getDatabaseMetaData().getRenderer(SqlRender.TYPE.UPDATE);
|
||||
return render.render(tableMetaData, param).getSql();
|
||||
}
|
||||
|
||||
public String buildSelectFields(String resultMapId, String tableName, Object arg) {
|
||||
QueryParam param = null;
|
||||
if (arg instanceof QueryParam) {
|
||||
param = ((QueryParam) arg);
|
||||
if (param.isPaging()) {
|
||||
if (Pager.get() == null) {
|
||||
Pager.doPaging(param.getPageIndex(), param.getPageSize());
|
||||
}
|
||||
} else {
|
||||
Pager.reset();
|
||||
}
|
||||
}
|
||||
if (param == null) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
RDBDatabaseMetaData databaseMetaDate = getActiveDatabase();
|
||||
Dialect dialect = databaseMetaDate.getDialect();
|
||||
CommonSqlRender render = (CommonSqlRender) databaseMetaDate.getRenderer(SqlRender.TYPE.SELECT);
|
||||
List<CommonSqlRender.OperationColumn> columns = render.parseOperationField(tableMetaData, param);
|
||||
SqlAppender appender = new SqlAppender();
|
||||
columns.forEach(column -> {
|
||||
RDBColumnMetaData columnMetaData = column.getRDBColumnMetaData();
|
||||
if (columnMetaData == null) {
|
||||
return;
|
||||
}
|
||||
String cname = columnMetaData.getName();
|
||||
if (!cname.contains(".")) {
|
||||
cname = tableMetaData.getName().concat(".").concat(cname);
|
||||
}
|
||||
boolean isJpa = columnMetaData.getProperty("fromJpa", false).isTrue();
|
||||
|
||||
appender.add(",", encodeColumn(dialect, cname)
|
||||
, " AS "
|
||||
, dialect.getQuoteStart()
|
||||
, isJpa ? columnMetaData.getAlias() : columnMetaData.getName()
|
||||
, dialect.getQuoteEnd());
|
||||
});
|
||||
param.getIncludes().remove("*");
|
||||
if (appender.isEmpty()) {
|
||||
return "*";
|
||||
}
|
||||
appender.removeFirst();
|
||||
return appender.toString();
|
||||
}
|
||||
|
||||
public String buildOrder(String resultMapId, String tableName, Object arg) {
|
||||
QueryParam param = null;
|
||||
if (arg instanceof QueryParam) {
|
||||
param = ((QueryParam) arg);
|
||||
}
|
||||
if (param == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
SqlAppender appender = new SqlAppender(" order by ");
|
||||
param.getSorts()
|
||||
.forEach(sort -> {
|
||||
RDBColumnMetaData column = tableMetaData.getColumn(sort.getName());
|
||||
if (column == null) {
|
||||
column = tableMetaData.findColumn(sort.getName());
|
||||
}
|
||||
if (column == null) {
|
||||
return;
|
||||
}
|
||||
String cname = column.getName();
|
||||
if (!cname.contains(".")) {
|
||||
cname = tableMetaData.getName().concat(".").concat(cname);
|
||||
}
|
||||
appender.add(encodeColumn(tableMetaData.getDatabaseMetaData().getDialect(), cname), " ", sort.getOrder(), ",");
|
||||
});
|
||||
if (appender.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
appender.removeLast();
|
||||
return appender.toString();
|
||||
}
|
||||
|
||||
public String buildWhereForUpdate(String resultMapId, String tableName, List<Term> terms) {
|
||||
String where = buildWhere(resultMapId, tableName, terms);
|
||||
if (where.trim().isEmpty()) {
|
||||
throw new BusinessException("禁止执行无条件的更新操作");
|
||||
}
|
||||
return where;
|
||||
}
|
||||
|
||||
public String buildWhereForUpdate(String resultMapId, String tableName, Object param) {
|
||||
String where = buildWhere(resultMapId, tableName, param);
|
||||
if (where.trim().isEmpty()) {
|
||||
throw new BusinessException("禁止执行无条件的更新操作");
|
||||
}
|
||||
return where;
|
||||
}
|
||||
|
||||
public String buildWhere(String resultMapId, String tableName, Object param) {
|
||||
List<Term> terms;
|
||||
if (param instanceof Param) {
|
||||
terms = ((Param) param).getTerms();
|
||||
} else if (param instanceof Entity) {
|
||||
terms = SqlParamParser.parseQueryParam(param).getTerms();
|
||||
} else {
|
||||
terms = new ArrayList<>();
|
||||
}
|
||||
if (param instanceof QueryParam) {
|
||||
QueryParam queryParam = ((QueryParam) param);
|
||||
if (queryParam.isPaging()) {
|
||||
if (Pager.get() == null) {
|
||||
Pager.doPaging(queryParam.getPageIndex(), queryParam.getPageSize());
|
||||
}
|
||||
} else {
|
||||
Pager.reset();
|
||||
}
|
||||
}
|
||||
return buildWhere(resultMapId, tableName, terms);
|
||||
}
|
||||
|
||||
public String buildWhere(String resultMapId, String tableName, List<Term> terms) {
|
||||
RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);
|
||||
RDBDatabaseMetaData databaseMetaDate = getActiveDatabase();
|
||||
SimpleWhereSqlBuilder builder = new SimpleWhereSqlBuilder() {
|
||||
@Override
|
||||
public Dialect getDialect() {
|
||||
return databaseMetaDate.getDialect();
|
||||
}
|
||||
};
|
||||
SqlAppender appender = new SqlAppender();
|
||||
builder.buildWhere(tableMetaData, "", terms, appender, new HashSet<>());
|
||||
return appender.toString();
|
||||
}
|
||||
|
||||
static class MysqlMeta extends MysqlRDBDatabaseMetaData {
|
||||
MysqlMeta() {
|
||||
super();
|
||||
renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder());
|
||||
renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.MYSQL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return DataSourceHolder.databaseSwitcher().currentDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
static class OracleMeta extends OracleRDBDatabaseMetaData {
|
||||
OracleMeta() {
|
||||
super();
|
||||
renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder());
|
||||
renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.ORACLE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return DataSourceHolder.databaseSwitcher().currentDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
static class H2Meta extends H2RDBDatabaseMetaData {
|
||||
H2Meta() {
|
||||
super();
|
||||
renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder());
|
||||
renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.H2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return DataSourceHolder.databaseSwitcher().currentDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
static class PGMeta extends PGRDBDatabaseMetaData {
|
||||
PGMeta() {
|
||||
super();
|
||||
renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder());
|
||||
renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.POSTGRES));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return DataSourceHolder.databaseSwitcher().currentDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
static class MSSQLMeta extends MSSQLRDBDatabaseMetaData {
|
||||
MSSQLMeta() {
|
||||
super();
|
||||
renderMap.put(SqlRender.TYPE.INSERT, new InsertSqlBuilder());
|
||||
renderMap.put(SqlRender.TYPE.UPDATE, new UpdateSqlBuilder(Dialect.MSSQL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return DataSourceHolder.databaseSwitcher().currentDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.InsertParam;
|
||||
import org.hswebframework.ezorm.rdb.executor.SQL;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBTableMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.support.simple.SimpleInsertSqlRender;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class InsertSqlBuilder extends SimpleInsertSqlRender {
|
||||
@Override
|
||||
public SQL render(RDBTableMetaData metaData, InsertParam param) {
|
||||
RDBTableMetaData metaDataNew = metaData.clone();
|
||||
metaDataNew.setDatabaseMetaData(metaData.getDatabaseMetaData());
|
||||
metaDataNew.getColumns().stream()
|
||||
.filter(column -> column.getName().contains("."))
|
||||
.map(RDBColumnMetaData::getName)
|
||||
.forEach(metaDataNew::removeColumn);
|
||||
return super.render(metaDataNew, param);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlAppender getParamString(String prefix, String paramName, RDBColumnMetaData rdbColumnMetaData) {
|
||||
String typeHandler = rdbColumnMetaData.getProperty("typeHandler")
|
||||
.getValue();
|
||||
|
||||
return new SqlAppender().add("#{", paramName,
|
||||
",javaType=", EasyOrmSqlBuilder.getJavaType(rdbColumnMetaData.getJavaType()),
|
||||
",jdbcType=", rdbColumnMetaData.getJdbcType(),
|
||||
typeHandler != null ? ",typeHandler=" + typeHandler : "",
|
||||
"}");
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class SqlBuilder {
|
||||
public static final Object current() {
|
||||
return EasyOrmSqlBuilder.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.utils.StringUtils;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SqlParamParser {
|
||||
|
||||
public static QueryParamEntity parseQueryParam(Object param) {
|
||||
return new QueryParamParser().parse(param).get();
|
||||
}
|
||||
|
||||
private static class QueryParamParser {
|
||||
private Query<?, QueryParamEntity> query = Query.empty(new QueryParamEntity());
|
||||
|
||||
private BiConsumer<String, Object> consumer = (k, v) -> {
|
||||
if (k.endsWith("$or")) {
|
||||
k = k.substring(0, k.length() - 3);
|
||||
query.or(k, v);
|
||||
} else {
|
||||
query.and(k, v);
|
||||
}
|
||||
};
|
||||
|
||||
private QueryParamParser parse(Object obj) {
|
||||
if (obj instanceof Map) {
|
||||
((Map) obj).forEach((k, v) -> {
|
||||
String key = String.valueOf(k);
|
||||
if ("pageIndex".equals(key)) {
|
||||
query.getParam().setPageIndex(StringUtils.toInt(v));
|
||||
}
|
||||
if ("pageSize".equals(key)) {
|
||||
query.getParam().setPageSize(StringUtils.toInt(v));
|
||||
}
|
||||
if (v != null) {
|
||||
if (v instanceof Entity || v instanceof Map) {
|
||||
List<Term> terms = new QueryParamParser().parse(v).get().getTerms();
|
||||
Term term = new Term();
|
||||
term.setType(key.equalsIgnoreCase("or") ? Term.Type.or : Term.Type.and);
|
||||
term.setTerms(terms);
|
||||
query.getParam().getTerms().add(term);
|
||||
} else {
|
||||
consumer.accept(String.valueOf(key), v);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
parse(FastBeanCopier.copy(obj, new LinkedHashMap<>()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private QueryParamEntity get() {
|
||||
return query.getParam();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.JDBCType;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class TypeUtils {
|
||||
private static final List<Class> numberType = Arrays.asList(
|
||||
byte.class, Byte.class
|
||||
, short.class, Short.class
|
||||
, int.class, Integer.class
|
||||
, float.class, Float.class
|
||||
, double.class, Double.class
|
||||
, long.class, Long.class
|
||||
, BigDecimal.class, BigInteger.class
|
||||
);
|
||||
|
||||
private static final List<JDBCType> numberJdbcType = Arrays.asList(
|
||||
JDBCType.TINYINT, JDBCType.DECIMAL, JDBCType.NUMERIC,
|
||||
JDBCType.BIGINT, JDBCType.SMALLINT, JDBCType.INTEGER,
|
||||
JDBCType.DECIMAL, JDBCType.BIT
|
||||
);
|
||||
|
||||
public static boolean isNumberType(RDBColumnMetaData columnMetaData) {
|
||||
return numberType.contains(columnMetaData.getJavaType())
|
||||
|| Number.class.isAssignableFrom(columnMetaData.getJavaType())
|
||||
|| numberJdbcType.contains(columnMetaData.getJdbcType());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.UpdateParam;
|
||||
import org.hswebframework.ezorm.rdb.executor.SQL;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBTableMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.hswebframework.ezorm.rdb.render.support.simple.SimpleUpdateSqlRender;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class UpdateSqlBuilder extends SimpleUpdateSqlRender {
|
||||
public UpdateSqlBuilder(Dialect dialect) {
|
||||
super(dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQL render(RDBTableMetaData metaData, UpdateParam param) {
|
||||
RDBTableMetaData metaDataNew = metaData.clone();
|
||||
metaDataNew.setDatabaseMetaData(metaData.getDatabaseMetaData());
|
||||
|
||||
metaDataNew.getColumns().stream()
|
||||
.filter(column -> column.getName().contains("."))
|
||||
.map(RDBColumnMetaData::getName)
|
||||
.forEach(metaDataNew::removeColumn);
|
||||
return super.render(metaDataNew, param);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SqlAppender getParamString(String paramName, RDBColumnMetaData rdbColumnMetaData) {
|
||||
String typeHandler = rdbColumnMetaData.getProperty("typeHandler")
|
||||
.getValue();
|
||||
|
||||
return new SqlAppender().add("#{", paramName,
|
||||
",javaType=", EasyOrmSqlBuilder.getJavaType(rdbColumnMetaData.getJavaType()),
|
||||
",jdbcType=", rdbColumnMetaData.getJdbcType(),
|
||||
typeHandler != null ? ",typeHandler=" + typeHandler : "", "}");
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder.jpa;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
import org.hswebframework.ezorm.core.ValueConverter;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBTableMetaData;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.BooleanValueConverter;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.DateTimeConverter;
|
||||
import org.hswebframework.ezorm.rdb.meta.converter.NumberValueConverter;
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.dao.mybatis.builder.TypeUtils;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.JDBCType;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* jpa 注解解析器
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class JpaAnnotationParser {
|
||||
|
||||
private static final Map<Class, JDBCType> jdbcTypeMapping = new HashMap<>();
|
||||
|
||||
private static final List<BiFunction<Class, PropertyDescriptor, JDBCType>> jdbcTypeConvert = new ArrayList<>();
|
||||
|
||||
static {
|
||||
jdbcTypeMapping.put(String.class, JDBCType.VARCHAR);
|
||||
|
||||
jdbcTypeMapping.put(Byte.class, JDBCType.TINYINT);
|
||||
jdbcTypeMapping.put(byte.class, JDBCType.TINYINT);
|
||||
|
||||
jdbcTypeMapping.put(Short.class, JDBCType.INTEGER);
|
||||
jdbcTypeMapping.put(short.class, JDBCType.INTEGER);
|
||||
|
||||
jdbcTypeMapping.put(Integer.class, JDBCType.INTEGER);
|
||||
jdbcTypeMapping.put(int.class, JDBCType.INTEGER);
|
||||
|
||||
jdbcTypeMapping.put(Character.class, JDBCType.CHAR);
|
||||
jdbcTypeMapping.put(char.class, JDBCType.CHAR);
|
||||
|
||||
jdbcTypeMapping.put(Long.class, JDBCType.BIGINT);
|
||||
jdbcTypeMapping.put(long.class, JDBCType.BIGINT);
|
||||
|
||||
jdbcTypeMapping.put(Double.class, JDBCType.DECIMAL);
|
||||
jdbcTypeMapping.put(double.class, JDBCType.DECIMAL);
|
||||
|
||||
jdbcTypeMapping.put(Float.class, JDBCType.DECIMAL);
|
||||
jdbcTypeMapping.put(float.class, JDBCType.DECIMAL);
|
||||
|
||||
jdbcTypeMapping.put(Boolean.class, JDBCType.BIT);
|
||||
jdbcTypeMapping.put(boolean.class, JDBCType.BIT);
|
||||
|
||||
jdbcTypeMapping.put(byte[].class, JDBCType.BLOB);
|
||||
|
||||
jdbcTypeMapping.put(BigDecimal.class, JDBCType.DECIMAL);
|
||||
jdbcTypeMapping.put(BigInteger.class, JDBCType.INTEGER);
|
||||
|
||||
jdbcTypeMapping.put(Date.class, JDBCType.TIMESTAMP);
|
||||
jdbcTypeMapping.put(java.sql.Date.class, JDBCType.TIMESTAMP);
|
||||
jdbcTypeMapping.put(java.sql.Timestamp.class, JDBCType.TIMESTAMP);
|
||||
|
||||
jdbcTypeMapping.put(Object.class, JDBCType.VARCHAR);
|
||||
|
||||
jdbcTypeConvert.add((type, property) -> {
|
||||
Enumerated enumerated = getAnnotation(type, property, Enumerated.class);
|
||||
return enumerated != null ? JDBCType.VARCHAR : null;
|
||||
});
|
||||
jdbcTypeConvert.add((type, property) -> {
|
||||
Lob enumerated = getAnnotation(type, property, Lob.class);
|
||||
return enumerated != null ? JDBCType.CLOB : null;
|
||||
});
|
||||
|
||||
jdbcTypeConvert.add((type, property) -> {
|
||||
boolean isArray = type.isArray();
|
||||
if (isArray) {
|
||||
type = type.getComponentType();
|
||||
|
||||
}
|
||||
if (type.isEnum() && EnumDict.class.isAssignableFrom(type)) {
|
||||
Class genType = ClassUtils.getGenericType(type);
|
||||
if (isArray) {
|
||||
return JDBCType.BIGINT;
|
||||
}
|
||||
return jdbcTypeMapping.getOrDefault(genType, JDBCType.VARCHAR);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static List<RDBColumnMetaData> parseColumnMeta(String prefix, String columnName, Class entityClass) {
|
||||
|
||||
PropertyDescriptor[] descriptors = BeanUtilsBean.getInstance()
|
||||
.getPropertyUtils()
|
||||
.getPropertyDescriptors(entityClass);
|
||||
List<RDBColumnMetaData> columnMetaDataList = new ArrayList<>();
|
||||
|
||||
for (PropertyDescriptor descriptor : descriptors) {
|
||||
Column columnAnn = getAnnotation(entityClass, descriptor, Column.class);
|
||||
CollectionTable collectionTable = getAnnotation(entityClass, descriptor, CollectionTable.class);
|
||||
|
||||
if (columnAnn == null) {
|
||||
if (collectionTable != null) {
|
||||
columnMetaDataList.addAll(parseColumnMeta(descriptor.getName(), collectionTable.name(), descriptor.getPropertyType()));
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
String realName = StringUtils.hasText(columnAnn.name()) ? columnAnn.name() : descriptor.getName();
|
||||
String realAlias = StringUtils.hasText(prefix) ? prefix.concat(".").concat(descriptor.getName()) : descriptor.getName();
|
||||
|
||||
RDBColumnMetaData column = new RDBColumnMetaData();
|
||||
column.setName(StringUtils.hasText(columnName) ? columnName.concat(".").concat(realName) : realName);
|
||||
column.setAlias(realAlias);
|
||||
column.setLength(columnAnn.length());
|
||||
column.setPrecision(columnAnn.precision());
|
||||
column.setJavaType(descriptor.getPropertyType());
|
||||
if (!columnAnn.updatable()) {
|
||||
column.setProperty("read-only", true);
|
||||
}
|
||||
if (!columnAnn.nullable()) {
|
||||
column.setNotNull(true);
|
||||
}
|
||||
if (StringUtils.hasText(columnAnn.columnDefinition())) {
|
||||
column.setColumnDefinition(columnAnn.columnDefinition());
|
||||
}
|
||||
Class propertyType = descriptor.getPropertyType();
|
||||
|
||||
JDBCType type = jdbcTypeMapping.get(propertyType);
|
||||
if (type == null) {
|
||||
type = jdbcTypeConvert.stream()
|
||||
.map(func -> func.apply(entityClass, descriptor))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(JDBCType.OTHER);
|
||||
}
|
||||
column.setJdbcType(type);
|
||||
columnMetaDataList.add(column);
|
||||
}
|
||||
return columnMetaDataList;
|
||||
}
|
||||
|
||||
public static RDBTableMetaData parseMetaDataFromEntity(Class entityClass) {
|
||||
Table table = AnnotationUtils.findAnnotation(entityClass, Table.class);
|
||||
if (table == null) {
|
||||
return null;
|
||||
}
|
||||
RDBTableMetaData tableMetaData = new RDBTableMetaData();
|
||||
tableMetaData.setName(table.name());
|
||||
parseColumnMeta(null, null, entityClass).forEach(tableMetaData::addColumn);
|
||||
return tableMetaData;
|
||||
}
|
||||
|
||||
|
||||
private static <T extends Annotation> T getAnnotation(Class entityClass, PropertyDescriptor descriptor, Class<T> type) {
|
||||
T ann = null;
|
||||
try {
|
||||
Field field = entityClass.getDeclaredField(descriptor.getName());
|
||||
ann = AnnotationUtils.findAnnotation(field, type);
|
||||
} catch (@SuppressWarnings("all") NoSuchFieldException ignore) {
|
||||
if (entityClass.getSuperclass() != Object.class) {
|
||||
return getAnnotation(entityClass.getSuperclass(), descriptor, type);
|
||||
}
|
||||
}
|
||||
Method read = descriptor.getReadMethod(),
|
||||
write = descriptor.getWriteMethod();
|
||||
if (null == ann && read != null) {
|
||||
ann = AnnotationUtils.findAnnotation(read, type);
|
||||
}
|
||||
if (null == ann && write != null) {
|
||||
ann = AnnotationUtils.findAnnotation(write, type);
|
||||
}
|
||||
return ann;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.dynamic;
|
||||
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
|
||||
|
||||
public class DynamicDataSourceSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {
|
||||
@Override
|
||||
public SqlSessionFactory build(Configuration config) {
|
||||
return new DynamicSqlSessionFactory(config);
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.dynamic;
|
||||
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
import org.mybatis.spring.transaction.SpringManagedTransaction;
|
||||
import org.springframework.jdbc.datasource.ConnectionHolder;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hswebframework.web.datasource.DataSourceHolder.switcher;
|
||||
|
||||
/**
|
||||
* mybatis 同一事务,同一个mapper,动态数据源切换支持
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class DynamicSpringManagedTransaction implements Transaction {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(SpringManagedTransaction.class);
|
||||
|
||||
private Map<String, TransactionProxy> connectionMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 当前数据源对应的事务代理
|
||||
*
|
||||
* @return {@link TransactionProxy}
|
||||
*/
|
||||
protected TransactionProxy getProxy() {
|
||||
return connectionMap.get(switcher().currentDataSourceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个事务代理
|
||||
*
|
||||
* @param proxy
|
||||
*/
|
||||
protected void addProxy(TransactionProxy proxy) {
|
||||
connectionMap.put(switcher().currentDataSourceId(), proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有代理
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected Collection<TransactionProxy> getAllProxy() {
|
||||
return connectionMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
TransactionProxy proxy = getProxy();
|
||||
if (proxy != null) {
|
||||
return proxy.getConnection();
|
||||
}
|
||||
//根据当前激活的数据源 获取jdbc链接
|
||||
DataSource dataSource = DataSourceHolder.currentDataSource().getNative();
|
||||
String dsId = switcher().currentDataSourceId();
|
||||
Connection connection = DataSourceUtils.getConnection(dataSource);
|
||||
proxy = new TransactionProxy(dsId, connection, dataSource);
|
||||
addProxy(proxy);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"DataSource (" + (dsId == null ? "default" : dsId) + ") JDBC Connection ["
|
||||
+ connection
|
||||
+ "] will"
|
||||
+ (proxy.isConnectionTransactional ? " " : " not ")
|
||||
+ "be managed by Spring");
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
for (TransactionProxy proxy : getAllProxy()) {
|
||||
proxy.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
for (TransactionProxy proxy : getAllProxy()) {
|
||||
proxy.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
SQLException tmp = null;
|
||||
for (TransactionProxy proxy : getAllProxy()) {
|
||||
try {
|
||||
proxy.close();
|
||||
//保证每个链接都能被释放
|
||||
} catch (SQLException e) {
|
||||
tmp = e;
|
||||
}
|
||||
}
|
||||
connectionMap.clear();
|
||||
if (null != tmp) {
|
||||
throw tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTimeout() throws SQLException {
|
||||
return getProxy().getTimeout();
|
||||
}
|
||||
|
||||
class TransactionProxy implements Transaction {
|
||||
Connection connection;
|
||||
DataSource dataSource;
|
||||
boolean isConnectionTransactional;
|
||||
boolean autoCommit;
|
||||
String dataSourceId;
|
||||
|
||||
public TransactionProxy(String dataSourceId, Connection connection, DataSource dataSource) {
|
||||
this.connection = connection;
|
||||
this.dataSource = dataSource;
|
||||
this.dataSourceId = dataSourceId;
|
||||
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(connection, dataSource);
|
||||
try {
|
||||
this.autoCommit = connection.getAutoCommit();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Committing DataSource (" + (dataSourceId == null ? "default" : dataSourceId) + ") JDBC Connection [" + this.connection + "]");
|
||||
}
|
||||
this.connection.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Rolling back DataSource (" + dataSourceId + ") JDBC Connection [" + this.connection + "]");
|
||||
}
|
||||
this.connection.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
DataSourceUtils.releaseConnection(connection, dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTimeout() throws SQLException {
|
||||
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
|
||||
if (holder != null && holder.hasTimeout()) {
|
||||
return holder.getTimeToLiveInSeconds();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.dynamic;
|
||||
|
||||
import org.apache.ibatis.exceptions.ExceptionFactory;
|
||||
import org.apache.ibatis.executor.ErrorContext;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.session.*;
|
||||
import org.apache.ibatis.session.defaults.DefaultSqlSession;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.apache.ibatis.transaction.TransactionFactory;
|
||||
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
|
||||
import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class DynamicSqlSessionFactory implements SqlSessionFactory {
|
||||
private final Configuration configuration;
|
||||
|
||||
public DynamicSqlSessionFactory(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession() {
|
||||
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(boolean autoCommit) {
|
||||
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(ExecutorType execType) {
|
||||
return openSessionFromDataSource(execType, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(TransactionIsolationLevel level) {
|
||||
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
|
||||
return openSessionFromDataSource(execType, level, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
|
||||
return openSessionFromDataSource(execType, null, autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(Connection connection) {
|
||||
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSession openSession(ExecutorType execType, Connection connection) {
|
||||
return openSessionFromConnection(execType, connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
|
||||
Transaction tx = null;
|
||||
try {
|
||||
final Environment environment = getConfiguration().getEnvironment();
|
||||
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
|
||||
DataSource ds = DataSourceHolder.currentDataSource().getNative();
|
||||
if (ds == null) {
|
||||
ds = environment.getDataSource();
|
||||
}
|
||||
tx = transactionFactory.newTransaction(ds, level, autoCommit);
|
||||
final Executor executor = getConfiguration().newExecutor(tx, execType);
|
||||
return new DefaultSqlSession(getConfiguration(), executor, autoCommit);
|
||||
} catch (Exception e) {
|
||||
closeTransaction(tx); // may have fetched a connection so lets call close()
|
||||
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
}
|
||||
}
|
||||
|
||||
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
|
||||
try {
|
||||
boolean autoCommit;
|
||||
try {
|
||||
autoCommit = connection.getAutoCommit();
|
||||
} catch (SQLException e) {
|
||||
// Failover to true, as most poor drivers
|
||||
// or databases won't support transactions
|
||||
autoCommit = true;
|
||||
}
|
||||
final Environment environment = configuration.getEnvironment();
|
||||
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
|
||||
final Transaction tx = transactionFactory.newTransaction(connection);
|
||||
final Executor executor = configuration.newExecutor(tx, execType);
|
||||
return new DefaultSqlSession(configuration, executor, autoCommit);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
|
||||
} finally {
|
||||
ErrorContext.instance().reset();
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
|
||||
if (environment == null || environment.getTransactionFactory() == null) {
|
||||
return new ManagedTransactionFactory();
|
||||
}
|
||||
return environment.getTransactionFactory();
|
||||
}
|
||||
|
||||
private void closeTransaction(Transaction tx) {
|
||||
if (tx != null) {
|
||||
try {
|
||||
tx.close();
|
||||
} catch (SQLException ignore) {
|
||||
// Intentionally ignore. Prefer previous error.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Alias("jsonArrayHandler")
|
||||
@MappedTypes({List.class})
|
||||
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CLOB})
|
||||
@Slf4j
|
||||
public class JsonArrayHandler extends BaseTypeHandler<List<Object>> {
|
||||
|
||||
private List<Object> parseArray(String json) {
|
||||
if (!StringUtils.hasText(json)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parseArray(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parseArray(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parseArray(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parseArray(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, List<Object> parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, JSON.toJSONString(parameter, SerializerFeature.WriteClassName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, List<Object> parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, "[]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
@Alias("jsonMapHandler")
|
||||
@MappedTypes({Map.class})
|
||||
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CLOB})
|
||||
@Slf4j
|
||||
public class JsonMapHandler extends BaseTypeHandler<Map<String, Object>> {
|
||||
private Map<String, Object> parseObject(String json) {
|
||||
if (!StringUtils.hasText(json)) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parseObject(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parseObject(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parseObject(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parseObject(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, JSON.toJSONString(parameter, SerializerFeature.WriteClassName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, "{}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.handler;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
@Alias("jsonSetHandler")
|
||||
@MappedTypes({Set.class})
|
||||
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CLOB})
|
||||
@Slf4j
|
||||
public class JsonSetHandler extends BaseTypeHandler<Set> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<Object> parseSet(String json) {
|
||||
if (!StringUtils.hasText(json)) {
|
||||
return null;
|
||||
}
|
||||
return (Set) JSON.parseObject(json, Set.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parseSet(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parseSet(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parseSet(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, Set parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, JSON.toJSONString(parameter, SerializerFeature.WriteClassName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Set parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, "[]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.handler;
|
||||
|
||||
import org.apache.ibatis.type.*;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@Alias("numberBooleanTypeHandler")
|
||||
@MappedTypes({Boolean.class})
|
||||
@MappedJdbcTypes({JdbcType.NUMERIC, JdbcType.BOOLEAN})
|
||||
public class NumberBooleanTypeHandler implements TypeHandler<Object> {
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
|
||||
if (parameter == null) {
|
||||
ps.setNull(i, jdbcType.TYPE_CODE);
|
||||
return;
|
||||
}
|
||||
if(parameter instanceof Number){
|
||||
if (jdbcType == JdbcType.BOOLEAN) {
|
||||
ps.setBoolean(i, ((Number) parameter).intValue()==1);
|
||||
}else{
|
||||
ps.setInt(i,((Number) parameter).intValue());
|
||||
}
|
||||
}else{
|
||||
if (jdbcType == JdbcType.BOOLEAN) {
|
||||
ps.setBoolean(i, Boolean.TRUE.equals(parameter));
|
||||
} else {
|
||||
ps.setInt(i, Boolean.TRUE.equals(parameter) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return rs.getBoolean(columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return rs.getBoolean(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return cs.getBoolean(columnIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public abstract class AbstractSqlTermCustomizer implements SqlTermCustomizer {
|
||||
|
||||
@Getter
|
||||
protected final String termType;
|
||||
|
||||
@Override
|
||||
public Dialect[] forDialect() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String createColumnName(RDBColumnMetaData column, String tableAlias) {
|
||||
if (StringUtils.isEmpty(tableAlias)) {
|
||||
tableAlias = column.getTableMetaData().getAlias();
|
||||
}
|
||||
return column.getTableMetaData()
|
||||
.getDatabaseMetaData()
|
||||
.getDialect()
|
||||
.buildColumnName(tableAlias, column.getName());
|
||||
}
|
||||
|
||||
|
||||
protected ChangedTermValue createChangedTermValue(Term term) {
|
||||
if (term.getValue() instanceof ChangedTermValue) {
|
||||
return ((ChangedTermValue) term.getValue());
|
||||
} else {
|
||||
ChangedTermValue termValue = new ChangedTermValue(term.getValue(), term.getValue());
|
||||
term.setValue(termValue);
|
||||
return termValue;
|
||||
}
|
||||
}
|
||||
|
||||
protected Object appendCondition(List<Object> values, String wherePrefix, SqlAppender appender) {
|
||||
int len = values.size();
|
||||
if (len == 1) {
|
||||
appender.add("=#{", wherePrefix, ".value.value[0]}");
|
||||
} else {
|
||||
appender.add("in(");
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
appender.add(",");
|
||||
}
|
||||
appender.add("#{", wherePrefix, ".value.value[" + i + "]}");
|
||||
}
|
||||
appender.add(")");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ChangedTermValue implements Serializable {
|
||||
private static final long serialVersionUID = 6373611532663483048L;
|
||||
|
||||
private Object old;
|
||||
|
||||
private Object value;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public interface SqlTermCustomizer extends Dialect.TermTypeMapper {
|
||||
String getTermType();
|
||||
|
||||
Dialect[] forDialect();
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.RenderPhase;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.function.SqlFunction;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.term.BoostTermTypeMapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class TreeStructureSqlTermCustomizer extends AbstractSqlTermCustomizer {
|
||||
protected boolean not;
|
||||
|
||||
protected boolean parent;
|
||||
|
||||
public TreeStructureSqlTermCustomizer(String termType, boolean not, boolean parent) {
|
||||
super(termType);
|
||||
this.not = not;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
protected abstract String getTableName();
|
||||
|
||||
protected abstract List<String> getTreePathByTerm(List<Object> termValue);
|
||||
|
||||
@Override
|
||||
public SqlAppender accept(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
ChangedTermValue termValue = createChangedTermValue(term);
|
||||
Dialect dialect = column.getTableMetaData().getDatabaseMetaData().getDialect();
|
||||
List<String> paths;
|
||||
if (termValue.getOld() == termValue.getValue()) {
|
||||
List<Object> value = BoostTermTypeMapper.convertList(column, termValue.getOld());
|
||||
paths = getTreePathByTerm(value)
|
||||
.stream()
|
||||
.map(path -> path.concat("%"))
|
||||
.collect(Collectors.toList());
|
||||
termValue.setValue(paths);
|
||||
} else {
|
||||
paths = ((List) termValue.getValue());
|
||||
}
|
||||
|
||||
SqlAppender termCondition = new SqlAppender();
|
||||
|
||||
termCondition.add(not ? "not " : "", "exists(select 1 from ", getTableName(), " tmp where tmp.u_id = ", createColumnName(column, tableAlias));
|
||||
int len = paths.size();
|
||||
|
||||
if (len > 0) {
|
||||
termCondition.add(" and (");
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
termCondition.addSpc(" or");
|
||||
}
|
||||
if (parent) {
|
||||
SqlFunction function = dialect.getFunction(SqlFunction.concat);
|
||||
String concat;
|
||||
if (function == null) {
|
||||
concat = getTableName() + ".path";
|
||||
log.warn("数据库方言未支持concat函数,你可以调用Dialect.installFunction进行设置!");
|
||||
} else {
|
||||
concat = function.apply(SqlFunction.Param.of(RenderPhase.where, Arrays.asList("tmp.path", "'%'")));
|
||||
}
|
||||
termCondition.add("#{", wherePrefix, ".value.value[", i, "]}", " like ", concat);
|
||||
} else {
|
||||
termCondition.add("tmp.path like #{", wherePrefix, ".value.value[", i, "]}");
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
termCondition.add(")");
|
||||
}
|
||||
termCondition.add(")");
|
||||
|
||||
return termCondition;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper.dict;
|
||||
|
||||
import org.hswebframework.ezorm.core.OptionConverter;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.RenderPhase;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.function.SqlFunction;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.term.BoostTermTypeMapper;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.AbstractSqlTermCustomizer;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.ChangedTermValue;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
|
||||
import java.sql.JDBCType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public class DictInTermTypeMapper extends AbstractSqlTermCustomizer {
|
||||
|
||||
private boolean not;
|
||||
|
||||
public static final String USE_DICT_MASK_FLAG = "dict-mask";
|
||||
|
||||
public DictInTermTypeMapper(boolean not) {
|
||||
super(not ? TermType.nin : TermType.in);
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
private boolean support(RDBColumnMetaData column) {
|
||||
if(column.getJdbcType()== JDBCType.VARCHAR){
|
||||
return false;
|
||||
}
|
||||
Class type = column.getJavaType();
|
||||
if (type != null && type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
|
||||
return ((type != null && type.isEnum()
|
||||
&& EnumDict.class.isAssignableFrom(type)
|
||||
&& column.getJavaType().isArray())
|
||||
||
|
||||
(column.getProperty(USE_DICT_MASK_FLAG).isTrue()
|
||||
&& column.getOptionConverter() != null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private List<EnumDict> getAllOption(RDBColumnMetaData column) {
|
||||
Class type = column.getJavaType();
|
||||
if (null != type) {
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
if (type.isEnum() && EnumDict.class.isAssignableFrom(type)) {
|
||||
return (List) Arrays.asList(type.getEnumConstants());
|
||||
}
|
||||
}
|
||||
|
||||
OptionConverter converter = column.getOptionConverter();
|
||||
if (converter == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return (List) converter.getOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAppender accept(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
//不支持数据字典
|
||||
if (!support(column)) {
|
||||
return buildNotSupport(wherePrefix, term, column, tableAlias);
|
||||
}
|
||||
ChangedTermValue changedValue = createChangedTermValue(term);
|
||||
|
||||
boolean any = term.getOptions().contains("any");
|
||||
|
||||
List<Object> list = BoostTermTypeMapper.convertList(column, changedValue.getOld());
|
||||
|
||||
EnumDict[] dicts = getAllOption(column)
|
||||
.stream()
|
||||
.filter(d -> d.eq(list))
|
||||
.toArray(EnumDict[]::new);
|
||||
|
||||
changedValue.setValue(EnumDict.toMask(dicts));
|
||||
Dialect dialect = column.getTableMetaData().getDatabaseMetaData().getDialect();
|
||||
|
||||
String columnName = dialect.buildColumnName(tableAlias, column.getName());
|
||||
String where = "#{" + wherePrefix + ".value.value}";
|
||||
SqlFunction sqlFunction = dialect.getFunction(SqlFunction.bitand);
|
||||
|
||||
if (sqlFunction == null) {
|
||||
throw new UnsupportedOperationException("数据库不支持[BITAND]函数");
|
||||
}
|
||||
String bitAnd = sqlFunction.apply(SqlFunction.Param.of(RenderPhase.where, Arrays.asList(columnName, where)));
|
||||
|
||||
String n;
|
||||
if (any) {
|
||||
n = not ? "=" : "!=";
|
||||
} else {
|
||||
n = not ? "!=" : "=";
|
||||
}
|
||||
return new SqlAppender().add(bitAnd, n, any ? "0" : columnName);
|
||||
|
||||
}
|
||||
|
||||
protected SqlAppender buildNotSupport(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
ChangedTermValue changedValue = createChangedTermValue(term);
|
||||
Dialect dialect = column.getTableMetaData().getDatabaseMetaData().getDialect();
|
||||
|
||||
List<Object> values = BoostTermTypeMapper.convertList(column, changedValue.getOld());
|
||||
|
||||
changedValue.setValue(values);
|
||||
|
||||
String columnName = dialect.buildColumnName(tableAlias, column.getName());
|
||||
SqlAppender appender = new SqlAppender();
|
||||
appender.add(columnName, not ? " NOT " : " ").add("IN(");
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
appender.add("#{", wherePrefix, ".value.value[", i, "]}", ",");
|
||||
}
|
||||
appender.removeLast();
|
||||
appender.add(")");
|
||||
return appender;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.mapper.dict;
|
||||
|
||||
import org.hswebframework.ezorm.core.OptionConverter;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBColumnMetaData;
|
||||
import org.hswebframework.ezorm.rdb.render.SqlAppender;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.Dialect;
|
||||
import org.hswebframework.ezorm.rdb.render.dialect.term.BoostTermTypeMapper;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.AbstractSqlTermCustomizer;
|
||||
import org.hswebframework.web.dao.mybatis.mapper.ChangedTermValue;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
|
||||
import java.sql.JDBCType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hswebframework.web.dao.mybatis.mapper.dict.DictInTermTypeMapper.USE_DICT_MASK_FLAG;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public class DictTermTypeMapper extends AbstractSqlTermCustomizer {
|
||||
|
||||
private boolean not;
|
||||
|
||||
public DictTermTypeMapper(boolean not) {
|
||||
super(not ? TermType.not : TermType.eq);
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
private boolean support(RDBColumnMetaData column) {
|
||||
if (column.getJdbcType() == JDBCType.VARCHAR) {
|
||||
return false;
|
||||
}
|
||||
Class type = column.getJavaType();
|
||||
if (type != null && type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
return ((type != null && type.isEnum()
|
||||
&& EnumDict.class.isAssignableFrom(type)
|
||||
&& column.getJavaType().isArray())
|
||||
||
|
||||
(column.getProperty(USE_DICT_MASK_FLAG).isTrue() && column.getOptionConverter() != null));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private List<EnumDict> getAllOption(RDBColumnMetaData column) {
|
||||
Class type = column.getJavaType();
|
||||
if (null != type) {
|
||||
if (type.isArray()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
if (type.isEnum() && EnumDict.class.isAssignableFrom(type)) {
|
||||
return (List) Arrays.asList(type.getEnumConstants());
|
||||
}
|
||||
}
|
||||
|
||||
OptionConverter converter = column.getOptionConverter();
|
||||
if (converter == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return (List) converter.getOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAppender accept(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
//不支持数据字典
|
||||
if (!support(column)) {
|
||||
return buildNotSupport(wherePrefix, term, column, tableAlias);
|
||||
}
|
||||
ChangedTermValue changedValue = createChangedTermValue(term);
|
||||
|
||||
List<Object> list = BoostTermTypeMapper.convertList(column, changedValue.getOld());
|
||||
|
||||
EnumDict[] dicts = getAllOption(column)
|
||||
.stream()
|
||||
.filter(d -> d.eq(list))
|
||||
.toArray(EnumDict[]::new);
|
||||
|
||||
changedValue.setValue(EnumDict.toMask(dicts));
|
||||
Dialect dialect = column.getTableMetaData().getDatabaseMetaData().getDialect();
|
||||
String columnName = dialect.buildColumnName(tableAlias, column.getName());
|
||||
return new SqlAppender().add(columnName, not ? " != " : "=", "#{", wherePrefix, ".value.value}");
|
||||
}
|
||||
|
||||
protected SqlAppender buildNotSupport(String wherePrefix, Term term, RDBColumnMetaData column, String tableAlias) {
|
||||
ChangedTermValue termValue = createChangedTermValue(term);
|
||||
// fix https://github.com/hs-web/hsweb-framework/issues/102
|
||||
Object newValue = BoostTermTypeMapper.convertValue(column, termValue.getOld());
|
||||
termValue.setValue(newValue);
|
||||
|
||||
Dialect dialect = column.getTableMetaData().getDatabaseMetaData().getDialect();
|
||||
String columnName = dialect.buildColumnName(tableAlias, column.getName());
|
||||
SqlAppender appender = new SqlAppender();
|
||||
appender.add(columnName, not ? " != " : "=", "#{", wherePrefix, ".value.value}");
|
||||
return appender;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.plgins.pager;
|
||||
|
||||
|
||||
import org.hswebframework.web.ThreadLocalUtils;
|
||||
|
||||
/**
|
||||
* 分页插件,通过此接口进行分页操作
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see PagerInterceptor
|
||||
*/
|
||||
public interface Pager {
|
||||
int pageIndex();
|
||||
|
||||
int pageSize();
|
||||
|
||||
String threadLocalKey = "nowPager";
|
||||
|
||||
static Pager getAndReset() {
|
||||
try {
|
||||
return get();
|
||||
} finally {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
static Pager get() {
|
||||
return ThreadLocalUtils.get(threadLocalKey);
|
||||
}
|
||||
|
||||
static void reset() {
|
||||
ThreadLocalUtils.remove(threadLocalKey);
|
||||
}
|
||||
|
||||
static void doPaging(int pageIndex, int pageSize) {
|
||||
ThreadLocalUtils.put(threadLocalKey, new Pager() {
|
||||
@Override
|
||||
public int pageIndex() {
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int pageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void doPaging(int pageIndex, int pageSize, int total) {
|
||||
doPaging(pageIndex, pageSize);
|
||||
rePaging(total);
|
||||
}
|
||||
|
||||
static void rePaging(int total) {
|
||||
Pager pager = get();
|
||||
if (pager != null) {
|
||||
// 当前页没有数据后跳转到最后一页
|
||||
if (pager.pageIndex() != 0 && (pager.pageIndex() * pager.pageSize()) >= total) {
|
||||
int tmp = total / pager.pageSize();
|
||||
int pageIndex = total % pager.pageSize() == 0 ? tmp - 1 : tmp;
|
||||
doPaging(pageIndex, pager.pageSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.dao.mybatis.plgins.pager;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.web.dao.mybatis.builder.EasyOrmSqlBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
|
||||
RowBounds.class, ResultHandler.class})})
|
||||
@Component
|
||||
public class PagerInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation target) throws Throwable {
|
||||
return target.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
if (target instanceof StatementHandler) {
|
||||
StatementHandler statementHandler = (StatementHandler) target;
|
||||
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
|
||||
String sql = statementHandler.getBoundSql().getSql();
|
||||
Pager pager = Pager.getAndReset();
|
||||
|
||||
String lower = sql.trim();
|
||||
|
||||
if (lower.startsWith("select")) {
|
||||
if (lower.contains("count(")) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
String newSql = sql;
|
||||
if (pager != null) {
|
||||
newSql = EasyOrmSqlBuilder.getInstance()
|
||||
.getActiveDatabase().getDialect()
|
||||
.doPaging(sql, pager.pageIndex(), pager.pageSize());
|
||||
}
|
||||
Object queryEntity = statementHandler.getParameterHandler().getParameterObject();
|
||||
if (queryEntity instanceof QueryParam && ((QueryParam) queryEntity).isForUpdate()) {
|
||||
newSql = newSql + " for update";
|
||||
}
|
||||
metaStatementHandler.setValue("delegate.boundSql.sql", newSql);
|
||||
}
|
||||
|
||||
}
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "mybatis",
|
||||
"type": "org.hswebframework.web.dao.mybatis.MybatisProperties",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "mybatis.dynamic-datasource",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties",
|
||||
"description": "enable dynamicDatasource."
|
||||
},
|
||||
{
|
||||
"name": "mybatis.mapper-location-excludes",
|
||||
"type": "java.lang.String[]",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties",
|
||||
"description": "exclude mapperLocations."
|
||||
},
|
||||
{
|
||||
"name": "mybatis.check-config-location",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.check-config-location",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.check-config-location",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.check-config-location",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Check the config file exists.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "mybatis.config",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.config",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.config",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.config",
|
||||
"type": "java.lang.String",
|
||||
"description": "Config file path.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.executor-type",
|
||||
"type": "org.apache.ibatis.session.ExecutorType",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.executor-type",
|
||||
"type": "org.apache.ibatis.session.ExecutorType",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.executor-type",
|
||||
"type": "org.apache.ibatis.session.ExecutorType",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.executor-type",
|
||||
"type": "org.apache.ibatis.session.ExecutorType",
|
||||
"description": "Execution mode.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.mapper-locations",
|
||||
"type": "java.lang.String[]",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.mapper-locations",
|
||||
"type": "java.lang.String[]",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.mapper-locations",
|
||||
"type": "java.lang.String[]",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.mapper-locations",
|
||||
"type": "java.lang.String[]",
|
||||
"description": "Location of mybatis mapper files.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-aliases-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-aliases-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-aliases-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-aliases-package",
|
||||
"type": "java.lang.String",
|
||||
"description": "Package to scan domain objects.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-handlers-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-handlers-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-handlers-package",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
},
|
||||
{
|
||||
"name": "mybatis.type-handlers-package",
|
||||
"type": "java.lang.String",
|
||||
"description": "Package to scan handlers.",
|
||||
"sourceType": "org.hswebframework.web.dao.mybatis.MybatisProperties"
|
||||
}
|
||||
],
|
||||
"hints": []
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.hswebframework.web.dao.mybatis.MybatisDaoAutoConfiguration
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
~ /*
|
||||
~ * Copyright 2019 http://www.hswebframework.org
|
||||
~ *
|
||||
~ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ * you may not use this file except in compliance with the License.
|
||||
~ * You may obtain a copy of the License at
|
||||
~ *
|
||||
~ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ *
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ * See the License for the specific language governing permissions and
|
||||
~ * limitations under the License.
|
||||
~ */
|
||||
-->
|
||||
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="BasicMapper">
|
||||
<!--通用查询条件-->
|
||||
<sql id="buildWhere">
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildWhere(resultMapId,tableName,#this['_parameter'])}
|
||||
</sql>
|
||||
<sql id="buildWhereForUpdate">
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildWhereForUpdate(resultMapId,tableName,#this['_parameter'])}
|
||||
</sql>
|
||||
<!--生成查询字段-->
|
||||
<sql id="buildSelectField">
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildSelectFields(resultMapId,tableName,#this['_parameter'])}
|
||||
</sql>
|
||||
|
||||
<!--生成修改字段-->
|
||||
<sql id="buildUpdateField">
|
||||
<set>
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildUpdateFields(resultMapId,tableName,#this['_parameter'])}
|
||||
</set>
|
||||
</sql>
|
||||
|
||||
<!--生成排序字段-->
|
||||
<sql id="buildSortField">
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildOrder(resultMapId,tableName,#this['_parameter'])}
|
||||
</sql>
|
||||
|
||||
<sql id="switcher">
|
||||
<bind name="_fullTableName" value="tableName"/>
|
||||
<!--当前数据库-->
|
||||
<bind name="_databaseName"
|
||||
value="@org.hswebframework.web.datasource.DataSourceHolder@databaseSwitcher().currentDatabase()"/>
|
||||
<!--表全名前缀-->
|
||||
<bind name="_databasePrefix" value="''"/>
|
||||
|
||||
<!--当前表名-->
|
||||
<bind name="_currentTableName"
|
||||
value="@org.hswebframework.web.datasource.DataSourceHolder@tableSwitcher().getTable(tableName)"/>
|
||||
|
||||
<if test="_currentTableName==null">
|
||||
<bind name="_currentTableName" value="tableName"/>
|
||||
</if>
|
||||
<if test="_databaseName!=null">
|
||||
<bind name="_databasePrefix" value="_databaseName+'.'"/>
|
||||
</if>
|
||||
<if test="_currentTableName!=null">
|
||||
<bind name="_fullTableName" value="_databasePrefix+_currentTableName"/>
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<!--生成查询sql-->
|
||||
<sql id="buildSelectSql">
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
<trim>
|
||||
select
|
||||
<include refid="BasicMapper.buildSelectField"/>
|
||||
from ${_fullTableName}
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
<include refid="BasicMapper.buildSortField"/>
|
||||
</trim>
|
||||
</sql>
|
||||
|
||||
<!--生成删除sql-->
|
||||
<sql id="buildDeleteSql">
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
<trim>
|
||||
delete from ${_fullTableName}
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhereForUpdate"/>
|
||||
</where>
|
||||
</trim>
|
||||
</sql>
|
||||
|
||||
<!--生成InsertSql-->
|
||||
<sql id="buildInsertSql">
|
||||
${@org.hswebframework.web.dao.mybatis.builder.SqlBuilder@current().buildInsertSql(resultMapId,tableName,#this['_parameter'])}
|
||||
</sql>
|
||||
|
||||
<!--生成UpdateSql-->
|
||||
<sql id="buildUpdateSql">
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
<trim>
|
||||
update ${_fullTableName}
|
||||
<include refid="BasicMapper.buildUpdateField"/>
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhereForUpdate"/>
|
||||
</where>
|
||||
</trim>
|
||||
</sql>
|
||||
|
||||
<!--生成查询数量sql-->
|
||||
<sql id="buildTotalSql">
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
<trim>
|
||||
select count(0) as total from ${_fullTableName}
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
</trim>
|
||||
</sql>
|
||||
</mapper>
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataType implements EnumDict<Byte> {
|
||||
TYPE1((byte) 1, "类型1"),
|
||||
TYPE2((byte) 2, "类型2"),
|
||||
TYPE3((byte) 3, "类型3"),
|
||||
TYPE4((byte) 4, "类型4");
|
||||
|
||||
private Byte value;
|
||||
|
||||
private String text;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since
|
||||
*/
|
||||
@Data
|
||||
public class NestEntity {
|
||||
|
||||
@Column
|
||||
private String name;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.apache.ibatis.session.TransactionIsolationLevel;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.hswebframework.web.dao.Dao;
|
||||
import org.hswebframework.web.dao.mybatis.EnumDictHandlerRegister;
|
||||
import org.hswebframework.web.dao.mybatis.MybatisEntityFactory;
|
||||
import org.hswebframework.web.dao.mybatis.MybatisProperties;
|
||||
import org.hswebframework.web.dao.mybatis.MybatisUtils;
|
||||
import org.hswebframework.web.dao.mybatis.builder.EasyOrmSqlBuilder;
|
||||
import org.hswebframework.web.dao.mybatis.dynamic.DynamicDataSourceSqlSessionFactoryBuilder;
|
||||
import org.hswebframework.web.dao.mybatis.dynamic.DynamicSpringManagedTransaction;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
||||
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SpringBootApplication
|
||||
//@EntityScan("org.hswebframework.web.dao")
|
||||
|
||||
public class TestApplication {
|
||||
|
||||
@Bean
|
||||
public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource") DataSource dataSource) throws Exception {
|
||||
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
|
||||
factory.setVfs(SpringBootVFS.class);
|
||||
factory.setDataSource(dataSource);
|
||||
String typeHandlers = "org.hswebframework.web.dao.mybatis.handler";
|
||||
factory.setTypeHandlersPackage(typeHandlers);
|
||||
factory.setMapperLocations(new Resource[]{new ClassPathResource("org/hswebframework/web/dao/test/TestDao.xml")});
|
||||
|
||||
SqlSessionFactory sqlSessionFactory = factory.getObject();
|
||||
return sqlSessionFactory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.executor.SqlExecutor;
|
||||
import org.hswebframework.web.commons.entity.param.DeleteParamEntity;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.commons.entity.param.UpdateParamEntity;
|
||||
import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TestApplication.class)
|
||||
public class TestCrud extends AbstractTransactionalJUnit4SpringContextTests {
|
||||
|
||||
@Autowired
|
||||
private TestDao testDao;
|
||||
|
||||
@Autowired
|
||||
private SqlExecutor sqlExecutor;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("sqlSessionFactory2")
|
||||
SqlSessionFactory sqlSessionFactory2;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("sqlSessionFactory")
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
@Before
|
||||
public void init() throws SQLException {
|
||||
sqlExecutor.exec("\n" +
|
||||
"create table h_test(\n" +
|
||||
" id BIGINT AUTO_INCREMENT PRIMARY KEY,\n" +
|
||||
" name VARCHAR(32) ,\n" +
|
||||
" create_time DATETIME,\n" +
|
||||
" data_type SMALLINT,\n" +
|
||||
" data_types BIGINT\n" +
|
||||
")");
|
||||
sqlExecutor.exec("\n" +
|
||||
"create table h_nest_table(\n" +
|
||||
" id BIGINT PRIMARY KEY,\n" +
|
||||
" name VARCHAR(32)\n" +
|
||||
")");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testCRUD() {
|
||||
|
||||
DataSourceHolder.databaseSwitcher().use("PUBLIC");
|
||||
|
||||
TestEntity entity = new TestEntity();
|
||||
entity.setName("测试");
|
||||
entity.setDataType(DataType.TYPE1);
|
||||
entity.setDataTypes(new DataType[]{DataType.TYPE1, DataType.TYPE3});
|
||||
testDao.insert(entity);
|
||||
Assert.assertNotNull(entity.getId());
|
||||
sqlExecutor.insert("insert into h_nest_table (id,name) values(#{id},'1234')",entity);
|
||||
|
||||
QueryParamEntity query = new QueryParamEntity();
|
||||
//any in
|
||||
query.where("dataTypes$in$any", Arrays.asList(DataType.TYPE1, DataType.TYPE2));
|
||||
|
||||
//#102
|
||||
//query.where("createTime", "2017-11-10");
|
||||
|
||||
|
||||
// DataSourceHolder.tableSwitcher().use("h_test", "h_test2");
|
||||
List<TestEntity> entities = testDao.queryNest(query);
|
||||
query.includes("name");
|
||||
testDao.count(query);
|
||||
testDao.query(query);
|
||||
|
||||
query.includes("nest.name", "*");
|
||||
testDao.countNest(query);
|
||||
|
||||
UpdateParamEntity.newUpdate()
|
||||
.set("name","测试")
|
||||
.set(entity::getDataType)
|
||||
.where("id",entity.getId())
|
||||
.exec(testDao::update);
|
||||
|
||||
DeleteParamEntity.newDelete()
|
||||
.where("id", "1234")
|
||||
.exec(testDao::delete);
|
||||
System.out.println(entities);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TestDao extends CrudDao<TestEntity, Long> {
|
||||
List<TestEntity> queryNest(Entity queryEntity);
|
||||
|
||||
int countNest(Entity queryEntity);
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package org.hswebframework.web.dao.crud;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "h_test")
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class TestEntity implements org.hswebframework.web.commons.entity.Entity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(
|
||||
strategy = GenerationType.IDENTITY
|
||||
)
|
||||
private Long id;
|
||||
|
||||
@Column(
|
||||
name = "name",
|
||||
columnDefinition = "varchar COMMENT '创建时间'"
|
||||
)
|
||||
private String name;
|
||||
|
||||
@Column(
|
||||
name = "create_time",
|
||||
columnDefinition = "timestamp COMMENT '创建时间'"
|
||||
)
|
||||
private Date createTime;
|
||||
|
||||
@Column(
|
||||
name = "data_type",
|
||||
columnDefinition = "bigint COMMENT '类型'"
|
||||
)
|
||||
private DataType dataType;
|
||||
|
||||
@Column(
|
||||
name = "data_types",
|
||||
columnDefinition = "bigint COMMENT '多个类型'"
|
||||
)
|
||||
private DataType[] dataTypes;
|
||||
|
||||
@CollectionTable(name = "nest_table")
|
||||
private NestEntity nest;
|
||||
|
||||
@CollectionTable(name = "nest_table2")
|
||||
private NestEntity nest2;
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.*;
|
||||
import org.hswebframework.ezorm.core.param.Term;
|
||||
import org.hswebframework.web.commons.entity.QueryEntity;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.asm.ClassReader;
|
||||
|
||||
import java.lang.invoke.LambdaMetafactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SqlParamParserTest {
|
||||
@SneakyThrows
|
||||
public static <T> void test(Function<T, Object> function) {
|
||||
Class t=function.getClass();
|
||||
|
||||
System.out.println(t);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test(TestQueryEntity::getName$like);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseQueryParam() {
|
||||
Map<String, Object> queryParam = new LinkedHashMap<>();
|
||||
queryParam.put("name", "张三");
|
||||
queryParam.put("name$like$or", "王五");
|
||||
queryParam.put("and", TestQueryEntity
|
||||
.builder()
|
||||
.name$like("李四%").age$gt(1)
|
||||
.or(TestQueryEntity.builder().name$like("王五").age$gt(10).build())
|
||||
.build());
|
||||
|
||||
QueryParamEntity entity = SqlParamParser.parseQueryParam(queryParam);
|
||||
|
||||
Assert.assertTrue(!entity.getTerms().isEmpty());
|
||||
Assert.assertEquals(entity.getTerms().get(0).getColumn(), "name");
|
||||
Assert.assertEquals(entity.getTerms().get(0).getType(), Term.Type.and);
|
||||
|
||||
Assert.assertEquals(entity.getTerms().get(1).getColumn(), "name");
|
||||
Assert.assertEquals(entity.getTerms().get(1).getTermType(), "like");
|
||||
Assert.assertEquals(entity.getTerms().get(1).getType(), Term.Type.or);
|
||||
|
||||
|
||||
Assert.assertEquals(entity.getTerms().get(2).getType(), Term.Type.and);
|
||||
Assert.assertTrue(!entity.getTerms().get(2).getTerms().isEmpty());
|
||||
Assert.assertEquals(entity.getTerms().get(2).getTerms().get(0).getTermType(), "like");
|
||||
|
||||
Assert.assertEquals(entity.getTerms().get(2).getTerms().get(1).getTermType(), "gt");
|
||||
|
||||
Assert.assertTrue(!entity.getTerms().get(2).getTerms().get(2).getTerms().isEmpty());
|
||||
Assert.assertEquals(entity.getTerms().get(2).getTerms().get(2).getTerms().get(0).getTermType(), "like");
|
||||
Assert.assertEquals(entity.getTerms().get(2).getTerms().get(2).getTerms().get(1).getTermType(), "gt");
|
||||
|
||||
System.out.println(JSON.toJSONString(entity, SerializerFeature.PrettyFormat));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hswebframework.web.commons.entity.QueryEntity;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TestQueryEntity implements QueryEntity {
|
||||
|
||||
private String name$like;
|
||||
|
||||
private int age$gt;
|
||||
|
||||
private TestQueryEntity or;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder.jpa;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
||||
/**
|
||||
* TODO 完成注释
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AbstractEntity {
|
||||
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder.jpa;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.meta.RDBTableMetaData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* TODO 完成注释
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since
|
||||
*/
|
||||
public class JpaAnnotationParserTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
RDBTableMetaData metaData = JpaAnnotationParser.parseMetaDataFromEntity(TestEntity.class);
|
||||
|
||||
Assert.assertNotNull(metaData);
|
||||
Assert.assertEquals(metaData.getColumns().size(), 5);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.hswebframework.web.dao.mybatis.builder.jpa;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hswebframework.web.dict.defaults.TrueOrFalse;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@Table(name = "s_test")
|
||||
@Data
|
||||
public class TestEntity extends AbstractEntity {
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "age")
|
||||
private Integer age;
|
||||
|
||||
@Column(name = "role_id")
|
||||
private String roleId;
|
||||
|
||||
@Column(name = "enabled")
|
||||
private TrueOrFalse enabled;
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
logging:
|
||||
level:
|
||||
org.hswebframework: DEBUG
|
||||
org.hswebframework.expands: ERROR
|
||||
com.ruiqi: DEBUG
|
||||
access-logger: INFO
|
||||
com.netflix: ERROR
|
||||
spring:
|
||||
aop:
|
||||
auto: true
|
||||
proxy-target-class: true
|
||||
profiles:
|
||||
active: dev
|
||||
datasource:
|
||||
url: jdbc:h2:mem:test
|
||||
username : sa
|
||||
password :
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
jpa:
|
||||
generate-ddl: true
|
||||
show-sql: true
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
mybatis:
|
||||
mapper-locations: org/hswebframework/web/dao/test/*.xml
|
||||
dynamic-datasource: true
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="org.hswebframework.web.dao.crud.TestDao">
|
||||
|
||||
<resultMap id="TestResultMap" type="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<id property="id" column="id" javaType="Long" jdbcType="INTEGER"/>
|
||||
<id property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
|
||||
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="TestNestResultMap" type="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<id property="id" column="id" javaType="Long" jdbcType="INTEGER"/>
|
||||
<!-- <result property="nest2.name" column="nest_table2.name" javaType="String" jdbcType="VARCHAR"/>-->
|
||||
<!-- <association property="nest" javaType="org.hswebframework.web.dao.crud.NestEntity" columnPrefix="nest_table." column="nest_table">-->
|
||||
<!-- <result property="name" column="name" jdbcType="VARCHAR" javaType="String"/>-->
|
||||
<!-- </association>-->
|
||||
<!-- <association property="nest2" javaType="org.hswebframework.web.dao.crud.NestEntity" columnPrefix="nest_table2." column="nest_table2">-->
|
||||
<!-- <result property="name" column="name" jdbcType="VARCHAR" javaType="String"/>-->
|
||||
<!-- </association>-->
|
||||
</resultMap>
|
||||
|
||||
<!--用于动态生成sql所需的配置-->
|
||||
<sql id="config">
|
||||
<bind name="resultMapId" value="'TestResultMap'"/>
|
||||
<bind name="tableName" value="'h_test'"/>
|
||||
</sql>
|
||||
|
||||
<insert id="insert" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
|
||||
parameterType="org.hswebframework.web.dao.crud.TestEntity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildInsertSql"/>
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="org.hswebframework.web.commons.entity.Entity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildUpdateSql"/>
|
||||
</update>
|
||||
|
||||
<update id="delete" parameterType="org.hswebframework.web.commons.entity.Entity">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildDeleteSql"/>
|
||||
</update>
|
||||
|
||||
<select id="query" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="TestResultMap">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildSelectSql"/>
|
||||
</select>
|
||||
|
||||
<select id="count" parameterType="org.hswebframework.web.commons.entity.Entity" resultType="int">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.buildTotalSql"/>
|
||||
</select>
|
||||
|
||||
<select id="queryNest" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="TestNestResultMap">
|
||||
<bind name="tableName" value="'h_test'"/>
|
||||
<bind name="resultMapId" value="'TestNestResultMap'"/>
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
select
|
||||
<include refid="BasicMapper.buildSelectField"/>
|
||||
from ${_fullTableName} h_test
|
||||
left join ${_databasePrefix}h_nest_table nest_table on nest_table.id=h_test.id
|
||||
left join ${_databasePrefix}h_nest_table nest_table2 on nest_table2.id=h_test.id
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
<include refid="BasicMapper.buildSortField"/>
|
||||
</select>
|
||||
|
||||
<select id="countNest" parameterType="org.hswebframework.web.commons.entity.Entity" resultType="int">
|
||||
<include refid="config"/>
|
||||
<include refid="BasicMapper.switcher"/>
|
||||
select
|
||||
count(1)
|
||||
from ${_fullTableName} h_test
|
||||
left join ${_databasePrefix}h_nest_table nest_table on nest_table.id=h_test.id
|
||||
left join ${_databasePrefix}h_nest_table nest_table2 on nest_table2.id=h_test.id
|
||||
<where>
|
||||
<include refid="BasicMapper.buildWhere"/>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>3.0.10-SNAPSHOT</version>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user