JAVA开发在后台DAO层查找语句时,用sql好还是hql好?

JAVA开发在后台DAO层查找语句时,用sql好还是hql好?,第1张

当然用hql,hql将sql已经封装成为对象了。如果你需要保存一个学生,你只需要save一个学生对象就行了。

而sql你需要 *** 作一大堆!

www.cao055.com

一般设置 jdbc 或者 数据库连接池,跟具体的数据库 关系不大,只是更改方言等具体细节而已

下载个jdbc的包,放到lib路径里面

#b2bplatform.dialect=org.hibernate.dialect.MySQLDialect此处改为sqlserver的方言即可

b2bplatform.show_sql=true

b2bplatform.generateDdl=true

b2bplatform.minPoolSize=1

b2bplatform.maxPoolSize=10

b2bplatform.connection.url=jdbc:mysql://127.0.0.1:3306/b2b?characterEncoding=UTF-8这个地方要改下端口什么的

b2bplatform.connection.username=root

b2bplatform.connection.password=123456

b2bplatform.connection.driver_class=com.mysql.jdbc.Driver //这个地方改下驱动名称

今天和大家分享下mybatis的一个分页插件PageHelper,在讲解PageHelper之前我们需要先了解下mybatis的插件原理。PageHelper

的官方网站:https://github.com/pagehelper/Mybatis-PageHelper

一、Plugin接口

mybatis定义了一个插件接口org.apache.ibatis.plugin.Interceptor,任何自定义插件都需要实现这个接口PageHelper就实现了改接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package org.apache.ibatis.plugin

import java.util.Properties

/**

* @author Clinton Begin

*/

public interface Interceptor {

Object intercept(Invocation invocation) throws Throwable

Object plugin(Object target)

void setProperties(Properties properties)

}

1:intercept 拦截器,它将直接覆盖掉你真实拦截对象的方法

2:plugin方法它是一个生成动态代理对象的方法

3:setProperties它是允许你在使用插件的时候设置参数值。

看下com.github.pagehelper.PageHelper分页的实现了那些

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

* Mybatis拦截器方法

*

* @param invocation 拦截器入参

* @return 返回执行结果

* @throws Throwable 抛出异常

*/

public Object intercept(Invocation invocation) throws Throwable {

if (autoRuntimeDialect) {

SqlUtil sqlUtil = getSqlUtil(invocation)

return sqlUtil.processPage(invocation)

} else {

if (autoDialect) {

initSqlUtil(invocation)

}

return sqlUtil.processPage(invocation)

}

}

这个方法获取了是分页核心代码,重新构建了BoundSql对象下面会详细分析

1

2

3

4

5

6

7

8

9

10

11

12

13

/**

* 只拦截Executor

*

* @param target

* @return

*/

public Object plugin(Object target) {

if (target instanceof Executor) {

return Plugin.wrap(target, this)

} else {

return target

}

}

这个方法是正对Executor进行拦截

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

/**

* 设置属性值

*

* @param p 属性值

*/

public void setProperties(Properties p) {

checkVersion()

//多数据源时,获取jdbcurl后是否关闭数据源

String closeConn = p.getProperty("closeConn")

//解决#97

if(StringUtil.isNotEmpty(closeConn)){

this.closeConn = Boolean.parseBoolean(closeConn)

}

//初始化SqlUtil的PARAMS

SqlUtil.setParams(p.getProperty("params"))

//数据库方言

String dialect = p.getProperty("dialect")

String runtimeDialect = p.getProperty("autoRuntimeDialect")

if (StringUtil.isNotEmpty(runtimeDialect) &&runtimeDialect.equalsIgnoreCase("TRUE")) {

this.autoRuntimeDialect = true

this.autoDialect = false

this.properties = p

} else if (StringUtil.isEmpty(dialect)) {

autoDialect = true

this.properties = p

} else {

autoDialect = false

sqlUtil = new SqlUtil(dialect)

sqlUtil.setProperties(p)

}

}

基本的属性设置

二、Plugin初始化

初始化和所有mybatis的初始化一样的在之前的文章里面已经分析了 《Mybatis源码分析之SqlSessionFactory(一)》

1

2

3

4

5

6

7

8

9

10

11

private void pluginElement(XNode parent) throws Exception {

if (parent != null) {

for (XNode child : parent.getChildren()) {

String interceptor = child.getStringAttribute("interceptor")

Properties properties = child.getChildrenAsProperties()

Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance()

interceptorInstance.setProperties(properties)

configuration.addInterceptor(interceptorInstance)

}

}

}

这里是讲多个实例化的插件对象放入configuration,addInterceptor最终存放到一个list里面的,以为这可以同时存放多个Plugin

三、Plugin拦截

插件可以拦截mybatis的4大对象ParameterHandler、ResultSetHandler、StatementHandler、Executor,源码如下图

在Configuration类里面可以找到

PageHelper使用了Executor进行拦截,上面的的源码里面已经可以看到了。

我看下上图newExecutor方法

1

executor = (Executor) interceptorChain.pluginAll(executor)

这个是生产一个代理对象,生产了代理对象就运行带invoke方法

四、Plugin运行

mybatis自己带了Plugin方法,源码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

public class Plugin implements InvocationHandler {

private Object target

private Interceptor interceptor

private Map<Class<?>, Set<Method>>signatureMap

private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>>signatureMap) {

this.target = target

this.interceptor = interceptor

this.signatureMap = signatureMap

}

public static Object wrap(Object target, Interceptor interceptor) {

Map<Class<?>, Set<Method>>signatureMap = getSignatureMap(interceptor)

Class<?>type = target.getClass()

Class<?>[] interfaces = getAllInterfaces(type, signatureMap)

if (interfaces.length >0) {

return Proxy.newProxyInstance(

type.getClassLoader(),

interfaces,

new Plugin(target, interceptor, signatureMap))

}

return target

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

Set<Method>methods = signatureMap.get(method.getDeclaringClass())

if (methods != null &&methods.contains(method)) {

return interceptor.intercept(new Invocation(target, method, args))

}

return method.invoke(target, args)

} catch (Exception e) {

throw ExceptionUtil.unwrapThrowable(e)

}

}

private static Map<Class<?>, Set<Method>>getSignatureMap(Interceptor interceptor) {

Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class)

// issue #251

if (interceptsAnnotation == null) {

throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName())

}

Signature[] sigs = interceptsAnnotation.value()

Map<Class<?>, Set<Method>>signatureMap = new HashMap<Class<?>, Set<Method>>()

for (Signature sig : sigs) {

Set<Method>methods = signatureMap.get(sig.type())

if (methods == null) {

methods = new HashSet<Method>()

signatureMap.put(sig.type(), methods)

}

try {

Method method = sig.type().getMethod(sig.method(), sig.args())

methods.add(method)

} catch (NoSuchMethodException e) {

throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e)

}

}

return signatureMap

}

private static Class<?>[] getAllInterfaces(Class<?>type, Map<Class<?>, Set<Method>>signatureMap) {

Set<Class<?>>interfaces = new HashSet<Class<?>>()

while (type != null) {

for (Class<?>c : type.getInterfaces()) {

if (signatureMap.containsKey(c)) {

interfaces.add(c)

}

}

type = type.getSuperclass()

}

return interfaces.toArray(new Class<?>[interfaces.size()])

}

}

wrap方法是为了生成一个动态代理类。

invoke方法是代理绑定的方法,该方法首先判定签名类和方法是否存在,如果不存在则直接反射调度被拦截对象的方法,如果存在则调度插件的interceptor方法,这时候会初始化一个Invocation对象

我们在具体看下PageHelper,当执行到invoke后程序将跳转到PageHelper.intercept

1

2

3

4

5

6

7

8

9

10

11

public Object intercept(Invocation invocation) throws Throwable {

if (autoRuntimeDialect) {

SqlUtil sqlUtil = getSqlUtil(invocation)

return sqlUtil.processPage(invocation)

} else {

if (autoDialect) {

initSqlUtil(invocation)

}

return sqlUtil.processPage(invocation)

}

}

我们在来看sqlUtil.processPage方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

/**

* Mybatis拦截器方法,这一步嵌套为了在出现异常时也可以清空Threadlocal

*

* @param invocation 拦截器入参

* @return 返回执行结果

* @throws Throwable 抛出异常

*/

public Object processPage(Invocation invocation) throws Throwable {

try {

Object result = _processPage(invocation)

return result

} finally {

clearLocalPage()

}

}

继续跟进

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

/**

* Mybatis拦截器方法

*

* @param invocation 拦截器入参

* @return 返回执行结果

* @throws Throwable 抛出异常

*/

private Object _processPage(Invocation invocation) throws Throwable {

final Object[] args = invocation.getArgs()

Page page = null

//支持方法参数时,会先尝试获取Page

if (supportMethodsArguments) {

page = getPage(args)

}

//分页信息

RowBounds rowBounds = (RowBounds) args[2]

//支持方法参数时,如果page == null就说明没有分页条件,不需要分页查询

if ((supportMethodsArguments &&page == null)

//当不支持分页参数时,判断LocalPage和RowBounds判断是否需要分页

|| (!supportMethodsArguments &&SqlUtil.getLocalPage() == null &&rowBounds == RowBounds.DEFAULT)) {

return invocation.proceed()

} else {

//不支持分页参数时,page==null,这里需要获取

if (!supportMethodsArguments &&page == null) {

page = getPage(args)

}

return doProcessPage(invocation, page, args)

}

}

这些都只是分装page方法,真正的核心是doProcessPage

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

/**

* Mybatis拦截器方法

*

* @param invocation 拦截器入参

* @return 返回执行结果

* @throws Throwable 抛出异常

*/

private Page doProcessPage(Invocation invocation, Page page, Object[] args) throws Throwable {

//保存RowBounds状态

RowBounds rowBounds = (RowBounds) args[2]

//获取原始的ms

MappedStatement ms = (MappedStatement) args[0]

//判断并处理为PageSqlSource

if (!isPageSqlSource(ms)) {

processMappedStatement(ms)

}

//设置当前的parser,后面每次使用前都会set,ThreadLocal的值不会产生不良影响

((PageSqlSource)ms.getSqlSource()).setParser(parser)

try {

//忽略RowBounds-否则会进行Mybatis自带的内存分页

args[2] = RowBounds.DEFAULT

//如果只进行排序 或 pageSizeZero的判断

if (isQueryOnly(page)) {

return doQueryOnly(page, invocation)

}

//简单的通过total的值来判断是否进行count查询

if (page.isCount()) {

page.setCountSignal(Boolean.TRUE)

//替换MS

args[0] = msCountMap.get(ms.getId())

//查询总数

Object result = invocation.proceed()

//还原ms

args[0] = ms

//设置总数

page.setTotal((Integer) ((List) result).get(0))

if (page.getTotal() == 0) {

return page

}

} else {

page.setTotal(-1l)

}

//pageSize>0的时候执行分页查询,pageSize<=0的时候不执行相当于可能只返回了一个count

if (page.getPageSize() >0 &&

((rowBounds == RowBounds.DEFAULT &&page.getPageNum() >0)

|| rowBounds != RowBounds.DEFAULT)) {

//将参数中的MappedStatement替换为新的qs

page.setCountSignal(null)

BoundSql boundSql = ms.getBoundSql(args[1])

args[1] = parser.setPageParameter(ms, args[1], boundSql, page)

page.setCountSignal(Boolean.FALSE)

//执行分页查询

Object result = invocation.proceed()

//得到处理结果

page.addAll((List) result)

}

} finally {

((PageSqlSource)ms.getSqlSource()).removeParser()

}

//返回结果

return page

}

上面的有两个 Object result = invocation.proceed()执行,第一个是执行统计总条数,第二个是执行执行分页的查询的数据

里面用到了代理。最终第一回返回一个总条数,第二个把分页的数据得到。

五:PageHelper使用

以上讲解了Mybatis的插件原理和PageHelper相关的内部实现,下面具体讲讲PageHelper使用

1:先增加maven依赖:

1

2

3

4

5

<dependency>

<groupId>com.github.pagehelper</groupId>

<artifactId>pagehelper</artifactId>

<version>4.1.6</version>

</dependency

2:配置configuration.xml文件加入如下配置(plugins应该在environments的上面 )

1

2

3

4

5

6

7

8

9

10

11

12

<plugins>

<!-- PageHelper4.1.6 -->

<plugin interceptor="com.github.pagehelper.PageHelper">

<property name="dialect" value="mysql"/>

<property name="offsetAsPageNum" value="false"/>

<property name="rowBoundsWithCount" value="false"/>

<property name="pageSizeZero" value="true"/>

<property name="reasonable" value="false"/>

<property name="supportMethodsArguments" value="false"/>

<property name="returnPageInfo" value="none"/>

</plugin>

</plugins>

相关字段说明可以查看SqlUtilConfig源码里面都用说明

注意配置的时候顺序不能乱了否则报错

1

2

3

4

5

6

Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseExceptionlineNumber: 57columnNumber: 17元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。

at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:259)

at org.apache.ibatis.parsing.XPathParser.<init>(XPathParser.java:120)

at org.apache.ibatis.builder.xml.XMLConfigBuilder.<init>(XMLConfigBuilder.java:66)

at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:49)

... 2 more

意思是配置里面的节点顺序是properties->settings->typeAliases->typeHandlers->objectFactory->objectWrapperFactory->plugins->environments->databaseIdProvider->mappers plugins应该在environments之前objectWrapperFactory之后 这个顺序不能乱了


欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/sjk/6696513.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-26
下一篇 2023-03-26

发表评论

登录后才能评论

评论列表(0条)

保存