This commit is contained in:
zhou-hao
2019-09-24 15:29:17 +08:00
parent 1c763faf67
commit 29945567cf
298 changed files with 986 additions and 8380 deletions

View File

@@ -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>

View File

@@ -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();
}
/**

View File

@@ -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);
}
}

View File

@@ -18,7 +18,7 @@
package org.hswebframework.web.authorization;
import org.hswebframework.web.authorization.listener.event.AuthorizationInitializeEvent;
import org.hswebframework.web.authorization.events.AuthorizationInitializeEvent;
/**
* 授权信息初始化服务接口,使用该接口初始化用的权限信息

View File

@@ -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);
}

View File

@@ -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)) {

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -16,7 +16,7 @@
*
*/
package org.hswebframework.web.authorization.listener.event;
package org.hswebframework.web.authorization.events;
import org.springframework.context.ApplicationEvent;

View File

@@ -16,7 +16,7 @@
*
*/
package org.hswebframework.web.authorization.listener.event;
package org.hswebframework.web.authorization.events;
import java.util.function.Function;

View File

@@ -16,7 +16,7 @@
*
*/
package org.hswebframework.web.authorization.listener.event;
package org.hswebframework.web.authorization.events;
import java.util.function.Function;

View File

@@ -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;

View File

@@ -16,7 +16,7 @@
*
*/
package org.hswebframework.web.authorization.listener.event;
package org.hswebframework.web.authorization.events;
import java.util.function.Function;

View File

@@ -1,4 +1,4 @@
package org.hswebframework.web.authorization.listener.event;
package org.hswebframework.web.authorization.events;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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));
}

View 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>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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>

View File

@@ -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>
<!--注意:keyColumnkeyPropertyuseGeneratedKeys只有在数据库生成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`即可.

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
};
}
}

View File

@@ -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;
}
}

View File

@@ -1,13 +0,0 @@
package org.hswebframework.web.dao.mybatis;
/**
* 排除不需要加载的mapper.xml
*
* @author zhouhao
* @since 3.0
*/
public interface MybatisMapperCustomizer {
String[] getExcludes();
String[] getIncludes();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 : "",
"}");
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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 : "", "}");
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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.
}
}
}
}

View File

@@ -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<>();
}
}

View File

@@ -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<>();
}
}

View File

@@ -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<>();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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": []
}

View File

@@ -1,3 +0,0 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.hswebframework.web.dao.mybatis.MybatisDaoAutoConfiguration

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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