• 关于

    检查XML文件结构

    的搜索结果

回答

"DTD(Document Type Definition)即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。要使用DTD验证模式的时候需要在XML文件的头部声明。 XML Schema语言就是XSD(XML Schemas Definition)。XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。 在使用XML Schema文档对XML实例文档进行检验,除了要声明名称空间外(xmlns=http://www.Springframework.org/schema/beans),还必须指定该名称空间所对应的XML Schema文档的存储位置。通过schemaLocation属性来指定名称空间所对应的XMLSchema文档的存储位置,它包含两个部分,一部分是名称空间的URI,另一部分就是该名称空间所标识的XML Schema文件位置或URL地址(xsi:schemaLocation=""http://www.Springframework.org/schema/beans http://www.Springframework.org/schema/beans/Spring-beans.xsd)。"
星尘linger 2020-04-12 20:17:55 0 浏览量 回答数 0

问题

为什么说 SAX 比 DOM4J 解析 xml 性能低?:报错

目前公认的XML解析器性能最好的是 DOM4J 我的疑问: DOM4J 是基于DOM文档树结构来进行解析的,来解析xml文件之前,会将整个 XML文档装载入内存形成完整的 DOM数结构ÿ...
kun坤 2020-06-06 15:28:11 3 浏览量 回答数 1

回答

如果是程序员的问题,可以写一个进行静态检查工具,入参是表结构和 xml 文件,就可以分析出 SQL 是否走索引了。开发和测试阶段不用 SQL 的执行计划分析,线上会有 druid 分析执行过的 SQL 报告。就用 druid 的 AST 进行词法分析,我们还加了一些其他的 SQL 规范,就解析了一起过规则列表。 来源:云原生后端社区
Atom 2020-04-25 14:11:36 0 浏览量 回答数 0

阿里云爆款特惠专场,精选爆款产品低至0.55折!

1核2G ECS n4 89.64元/年,精选爆款产品低至0.55折!

问题

深入理解Magento - 第一章 - Magento强大的配置系统:报错 

深入理解Magento 作者: Alan Storm 翻译: zhlmmc 前言 我从2007年开始使用Magento,应该算是国内第一批使用Magento的用户。但是我却从来没有认真研究过Mag...
kun坤 2020-06-04 21:14:35 5 浏览量 回答数 1

问题

深入理解Magento - 第一章 - Magento强大的配置系统 - 配置报错

" <span class=""Apple-style-span"" style=""border-collapse: separate; color: #000...
montos 2020-06-04 15:18:56 4 浏览量 回答数 1

问题

详解 Spring 3.0 基于 Annotation 的依赖注入实现 配置报错 

作者:张 建平, 项目经理, iSoftStone Co.,Ltd 简介: Spring 的依赖配置方式与 Spring 框架的内核自身是松耦合设计的。然而,直到 Spring 3.0 以前...
kun坤 2020-06-01 09:44:47 3 浏览量 回答数 1

问题

grails最佳实践:报错

我工作于IntelliGrape, 一个专用Groovy&Grails开发项目的公司. 本文列举了我们Grails项目遵循的几个基本的最佳实踐, 它们通过邮件, Stack Overflow, 博客, podcasts, 和公司内部...
kun坤 2020-06-14 15:53:05 0 浏览量 回答数 1

问题

spring和Velocity的配置问题?报错

配置信息如下: <bean id="viewResolver" class="org.springframework.web.servlet.view.veloc...
爱吃鱼的程序员 2020-06-22 20:43:00 0 浏览量 回答数 1

问题

grails最佳实践:配置报错 

我工作于IntelliGrape, 一个专用Groovy&Grails开发项目的公司. 本文列举了我们Grails项目遵循的几个基本的最佳实踐, 它们通过邮件, Stack Overflow, 博客, podcasts, 和公司内部...
kun坤 2020-06-02 15:16:24 0 浏览量 回答数 1

回答

1.mapUnderscoreToCameCase 这个拼写错误,应该是'mapUnderscoreToCamelCase' 2. 自动扫描mapping.xml文件我是这么配置的.  <!--自动扫描mapping.xml文件--><propertyname="mapperLocations"value="classpath:mapper/*.xml"/> 你的spring-context.xml文件贴出来看看. 还真的是,我找到了文件比对的时候竟然没看出来,我看前面都是一样的,到接近后面的时候就粗心了一点 mapUnderscoreToCameCase这个没有mybatis-config.xml里面的东西都找不到,我把mapUnderscoreToCamelCase这个删除了也不行 mapUnderscoreToCameCase,这个东西是不是拼写错误,你检查一下删除了也不行,就会报其他错 目录结构 web.xml <?xmlversion="1.0"encoding="UTF-8"?><web-appversion="3.0"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"><filter><description>请求和应答字符编码过滤器</description><filter-name>encoding-filter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding-filter</filter-name><servlet-name>spring-mvc</servlet-name></filter-mapping><listener><description>启动spring容器</description><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param><param-name>contextConfigLocation</param-name><!--和spring-mvc里面的<param-name>contextConfigLocation</param-name>一样--><param-value>classpath:spring-context.xml</param-value></context-param><servlet><description>spring-mvc的前端控制器</description><servlet-name>spring-mvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>spring-mvc</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping><welcome-file-list><welcome-file>login.jsp</welcome-file></welcome-file-list></web-app> spring-mvc.xml <?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--扫描业务层组件--><context:component-scanbase-package="com.zap.vip.function"></context:component-scan><!--配置c3p0事务源--><beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource"><propertyname="driverClass"value="com.mysql.jdbc.Driver"></property><propertyname="jdbcUrl"value="jdbc:mysql://127.0.0.1:3306/vip"></property><propertyname="user"value="root"></property><propertyname="password"value="123456"></property><propertyname="minPoolSize"value="1"></property><propertyname="maxPoolSize"value="5"></property><propertyname="initialPoolSize"value="1"></property><propertyname="acquireIncrement"value="1"></property></bean><!--配置一个mysql事务管理器--><beanid="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"></property></bean><!--表示使用注解来控制事务--><tx:annotation-driventransaction-manager="transactionManager"></tx:annotation-driven><beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><propertyname="dataSource"ref="dataSource"></property><propertyname="configLocation"value="classpath:mybatis-config.xml"></property><propertyname="mapperLocations"><list><value>classpath:mapper/*.xml</value></list></property></bean><!--扫描生成所有的dao层对象--><beanclass="org.mybatis.spring.mapper.MapperScannerConfigurer"><propertyname="basePackage"value="com.zap.vip.dao"></property><propertyname="sqlSessionFactoryBeanName"value="sqlSessionFactory"></property></bean></beans> mybatis-config.xml <?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEconfigurationPUBLIC"-//mybatis.org//DTDConfig3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><settings><settingname="cacheEnable"value="true"/><settingname="defaultStatementTimeout"value="3000"></setting><settingname="mapUnderscoreToCamelCase"value="true"></setting><settingname="proxyFactory"value="CGLIB"></setting><settingname="lazyLoadingEnable"value="true"></setting></settings><plugins><plugininterceptor="com.github.pagehelper.PageHelper"><propertyname="dialect"value="mysql"></property><!--该参数默认为false--><!--设置true时,会将RowBounds第一个参数offset当成pageNum页码使用--><!--和starPage中的pageNum效果一样--><propertyname="offsetAsPageNum"value="true"></property><!--该参数默认为false--><!--设置为true时,使用RowBounds分页会进行count查询--><propertyname="rowBoundsWithCount"value="true"></property><!--设置为true时,如果pageSize=0或者RowBounds.limit=0就会查出全部的结果--><!--(相当于没有执行分页查询,但是返回结果仍然是Page类型)--><propertyname="pageSizeZero"value="true"></property></plugin></plugins></configuration> 从异常信息提示来看,是mybatis-config.xml文件中mapUnderscoreToCamelCase这个setting不认识,有可能是mybatis版本不对。我怎么觉得dtd文件有问题啊, 我还真的按照你那个异常的信息去找了mybatis和mybatis-spring里面对应的代码,真的有那个配置,那现在的问题就是是不是你用的jar的版本并没有那个api啊,我找的是mybatis-3.4.2以及mybatis-spring-1.3.1,你看下你的版本回复 @阿信sxq:你看下面的错误信息我把版本换成你说的了回复 @阿信sxq:org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'fun':Unsatisfieddependencyexpressedthroughfield'udao':Errorcreatingbeanwithn回复 @顶呱呱:那你升一下版本试试我的是3.4.1和1.3 org.springframework.beans.factory.UnsatisfiedDependencyException:Errorcreatingbeanwithname'fun':Unsatisfieddependencyexpressedthroughfield'udao':Errorcreatingbeanwithname'userDao'definedinfile[D:\vip\target\vip\WEB-INF\classes\com\zap\vip\dao\UserDao.class]:Cannotresolvereferencetobean'sqlSessionFactory'whilesettingbeanproperty'sqlSessionFactory';nestedexceptionisorg.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'sqlSessionFactory'definedinclasspathresource[spring-context.xml]:Invocationofinitmethodfailed;nestedexceptionisorg.springframework.core.NestedIOException:Failedtoparseconfigresource:classpathresource[mybatis-config.xml];nestedexceptionisorg.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).;nestedexceptionisorg.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'userDao'definedinfile[D:\vip\target\vip\WEB-INF\classes\com\zap\vip\dao\UserDao.class]:Cannotresolvereferencetobean'sqlSessionFactory'whilesettingbeanproperty'sqlSessionFactory';nestedexceptionisorg.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'sqlSessionFactory'definedinclasspathresource[spring-context.xml]:Invocationofinitmethodfailed;nestedexceptionisorg.springframework.core.NestedIOException:Failedtoparseconfigresource:classpathresource[mybatis-config.xml];nestedexceptionisorg.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)   atorg.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)   atorg.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)   atorg.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)   atorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)   atorg.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)   atorg.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)   atorg.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)   atorg.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)   atorg.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)   atorg.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444)   atorg.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326)   atorg.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)   atorg.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973)   atorg.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)   atorg.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)   atorg.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)   atorg.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)   atorg.apache.catalina.core.StandardHost.addChild(StandardHost.java:632)   atorg.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1740)   atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)   atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)   atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)   atjava.lang.reflect.Method.invoke(Method.java:601)   atorg.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)   atcom.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)   atcom.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:791)   atorg.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)   atorg.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565)   atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)   atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)   atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)   atjava.lang.reflect.Method.invoke(Method.java:601)   atorg.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)   atcom.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)   atcom.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:791)   atjavax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1486)   atjavax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:96)   atjavax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1327)   atjavax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1419)   atjavax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:847)   atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)   atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)   atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)   atjava.lang.reflect.Method.invoke(Method.java:601)   atsun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)   atsun.rmi.transport.Transport$1.run(Transport.java:177)   atsun.rmi.transport.Transport$1.run(Transport.java:174)   atjava.security.AccessController.doPrivileged(NativeMethod)   atsun.rmi.transport.Transport.serviceCall(Transport.java:173)   atsun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:553)   atsun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:808)   atsun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:667)   atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)   atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)   atjava.lang.Thread.run(Thread.java:722)Causedby:org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'userDao'definedinfile[D:\vip\target\vip\WEB-INF\classes\com\zap\vip\dao\UserDao.class]:Cannotresolvereferencetobean'sqlSessionFactory'whilesettingbeanproperty'sqlSessionFactory';nestedexceptionisorg.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'sqlSessionFactory'definedinclasspathresource[spring-context.xml]:Invocationofinitmethodfailed;nestedexceptionisorg.springframework.core.NestedIOException:Failedtoparseconfigresource:classpathresource[mybatis-config.xml];nestedexceptionisorg.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)   atorg.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1481)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1226)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)   atorg.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)   atorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)   atorg.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)   atorg.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)   atorg.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)   atorg.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1214)   atorg.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)   atorg.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019)   atorg.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)   ...58moreCausedby:org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'sqlSessionFactory'definedinclasspathresource[spring-context.xml]:Invocationofinitmethodfailed;nestedexceptionisorg.springframework.core.NestedIOException:Failedtoparseconfigresource:classpathresource[mybatis-config.xml];nestedexceptionisorg.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)   atorg.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)   atorg.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)   atorg.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)   atorg.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)   atorg.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)   ...72moreCausedby:org.springframework.core.NestedIOException:Failedtoparseconfigresource:classpathresource[mybatis-config.xml];nestedexceptionisorg.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:500)   atorg.mybatis.spring.SqlSessionFactoryBean.afterPropertiesSet(SqlSessionFactoryBean.java:380)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)   atorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)   ...79moreCausedby:org.apache.ibatis.builder.BuilderException:ErrorparsingSQLMapperConfiguration.Cause:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)   atorg.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)   atorg.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:494)   ...82moreCausedby:org.apache.ibatis.builder.BuilderException:ThesettingcacheEnableisnotknown. Makesureyouspelleditcorrectly(casesensitive).   atorg.apache.ibatis.builder.xml.XMLConfigBuilder.settingsAsProperties(XMLConfigBuilder.java:133)   atorg.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:106)   ...84more 应该是cacheEnabled,不是cacheEnableThesettingcacheEnableisnotknown.Makesureyouspelleditcorrectly(casesensitive),类似的问题,建议你把那些个setting看下,到底mybatis的哪个版本有哪些setting。其他的异常都是因为这个导致的。或者你把你的项目打个包发上来看看。 dtd路径颜色都没变 不过又可以点进去 经过上面的讨论,根据org.apache.ibatis.session.Configuration,你配置错误的包括把mapUnderscoreToCamelCase写成mapUnderscoreToCameCase,把cacheEnabled写成cacheEnable,并且,我看你cacheEnabled配置的true,默认值就是true啊,没必要配置的。 我还给你说哈springmvc的配置一般和spring整体的配置单独配置,生命周期那些有差异,数据库连接池等的配置在spring整体的配置里面回复 @顶呱呱:osc的maven早就关了回复 @阿信sxq:pom.xml里面的jar管理引用不了是osc的原因吗这根本不是所谓的“整合”出的问题,单独运行也运行不起嘛,而且我用spring+mybatis的时候也没有配置你配的那些啊回复 @阿信sxq:非常感谢了!帮我解决了问题回复 @myumen:的确是你们说的那些问题!非常感谢,初次接触框架整合。困扰好几天了,请问有哪些spring学习的交流群吗 问题解决就赶紧评出个最佳答案吧,保持良好的提问氛围~
爱吃鱼的程序员 2020-06-08 14:08:31 0 浏览量 回答数 0

问题

应用 AXIS 开始 Web 服务之旅:报错

一. 介绍 本文并不是想介绍Web服务的原理、系统架构等,我们假设您已经了解了关于Web服务的一些基本的概念、原理等知识。本文主要是针对那些已经了解Web服 务概念,但是还没有亲身体会Web服务...
kun坤 2020-06-08 11:01:46 3 浏览量 回答数 1

问题

grails最佳实践 - grails报错

" 我工作于IntelliGrape, 一个专用Groovy&Grails开发项目的公司. 本文列举了我们Grails项目遵循的几个基本的最佳实踐, 它们通过邮件, Stack Overflow, 博客, <a tar...
montos 2020-06-03 21:54:06 3 浏览量 回答数 1

问题

【精品问答】大数据常见技术问题100问

大数据常见技术问题100问 1.如何检查namenode是否正常运行?重启namenode的命令是什么? 2.hdfs存储机制是怎样的? 3.hadoop中combiner的作用是什么? 4.hadoop中combiner的作用是什...
珍宝珠 2020-02-17 13:02:59 19 浏览量 回答数 1

问题

深入理解Magento – 第七章 – 自定义Magento系统配置 :报错

深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第七章 – 自定义Magento系统配置 Magento拥有十分强大的后台管理系统。作为一名开发人员,这套...
kun坤 2020-06-14 15:21:31 0 浏览量 回答数 1

问题

第七章 – 自定义Magento系统配置:配置报错 

深入理解Magento – 深入理解Magento 作者: Alan Storm 翻译: Hailong Zhang 第七章 – 自定义Magento系统配置 Magento拥有十分强大的后台管理系统。作为一...
kun坤 2020-06-02 14:47:13 0 浏览量 回答数 1

问题

深入理解Magento – 第七章 – 自定义Magento系统配置 - 配置报错

" 深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第七章 – 自定义Magento系统配置 Magento拥有十分强大的后台管理系统。作为一名开发人员&#x...
montos 2020-06-03 20:29:30 2 浏览量 回答数 1

回答

本教程示例详细演示了如何控制用户对 OSS 存储空间和文件夹的访问。在示例中,我们首先创建一个存储空间和文件夹,然后使用阿里云主账号创建访问管理 (RAM) 用户,并为这些用户授予对所创建 OSS 存储空间及文件夹的增量权限。 存储空间和文件夹的基本概念 阿里云 OSS 的数据模型为扁平型结构,所有文件都直接隶属于其对应的存储空间。因此,OSS 缺少文件系统中类似于目录与子文件夹的层次结构。但是,您可以在 OSS 控制台上模拟文件夹层次结构。在该控制台中,您可以按文件夹对相关文件进行分组、分类和管理,如下图所示。     OSS 提供使用键值(key)对格式的分布式对象存储服务。用户根据其唯一的key(对象名)检索对象的内容。例如,名为 example-company 的存储空间有三个文件夹:Development、 Marketing 和 Private,以及一个对象 oss-dg.pdf。 在创建   Development  文件夹时,控制台会创建一个key为 Development/的对象。注意,文件夹的key包括分隔符   /。 当您将名为ProjectA.docx的对象上传到Development文件夹中时,控制台会上传该对象并将其key设置为Development/ProjectA.docx。 在该key中,Development为前缀,而/为分隔符。您可以从存储空间中获取具有特定前缀和分隔符的所有对象的列表。在控制台中,单击Development文件夹时,控制台会列出文件夹中的对象,如下图所示。 说明  当控制台列出   example-company存储空间中的   Development文件夹时,它会向 OSS 发送一个用于指定前缀   Development和分隔符   /的请求。控制台的响应与文件系统类似,会显示文件夹列表。上例说明,存储空间 example-company有三个对象,其key分别为   Development/Alibaba Cloud.pdf、 Development/ProjectA.docx及   Development/ProjectB.docx。 控制台通过对象的key推断逻辑层次结构。当您创建对象的逻辑层次结构时,您可以管理对个别文件夹的访问,如本教程后面描述的那样。 在本教程开始之前,您还需要知道“根级”存储空间内容的概念。假设example-company存储空间包含以下对象: Development/Alibaba Cloud.pdf Development/ProjectA.docx Development/ProjectB.docx Marketing/data2016.xlsx Marketing/data2016.xlsx Private/2017/images.zip Private/2017/promote.pptx oss-dg.pdf 这些对象的key构建了一个以 Development、Marketing 和 Private 作为根级文件夹并以 oss-dg.pdf 作为根级对象的逻辑层次结构。当您单击 OSS 控制台中的存储空间名时,控制台会将一级前缀和一个分隔符(Development/、Marketing/ 和 Private/)显示为根级文件夹。对象 oss-dg.pdf 没有前缀,因此显示为根级别项。   OSS 的请求和响应逻辑 在授予权限之前,我们需要清楚,当用户单击某个存储空间的名字时控制台向 OSS 发送的是什么请求、OSS 返回的是什么响应,以及控制台如何解析该响应。 当用户单击某个存储空间名时,控制台会将GetBucket 请求发送至 OSS。此请求包括以下参数: prefix,其值为空字符串。 delimiter,其值为 /。 请求示例如下所示: GET /?prefix=&delimiter=/ HTTP/1.1 Host: example-company.oss-cn-hangzhou.aliyuncs.com Date: Fri, 24 Feb 2012 08:43:27 GMT Authorization: OSS qn6qrrqxo2oawuk53otfjbyc:DNrnx7xHk3sgysx7I8U9I9IY1vY= OSS 返回的响应包括ListBucketResult元素: HTTP/1.1 200 OK x-oss-request-id: 534B371674E88A4D8906008B Date: Fri, 24 Feb 2012 08:43:27 GMT Content-Type: application/xml Content-Length: 712 Connection: keep-alive Server: AliyunOSS <?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns=¡±http://doc.oss-cn-hangzhou.aliyuncs.com¡±><Name>example-company</Name><Prefix></Prefix><Marker></Marker><MaxKeys>100</MaxKeys><Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <Contents> <Key>oss-dg.pdf</Key> ... </Contents> <CommonPrefixes> <Prefix>Development</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>Marketing</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>Private</Prefix> </CommonPrefixes></ListBucketResult> 由于 oss-dg.pdf 不包含/分隔符,因此 OSS 在<Contents/>元素中返回该key。存储空间 example-company 中的所有其他key都包含/分隔符,因此 OSS 会将这些key分组,并为每个前缀值 Development/、Marketing/ 和 Private/ 返回一个<CommonPrefixes/>元素。该元素是一个字符串,包含从这些key的第一个字符开始到第一次出现指定的/分隔符之间的字符。 控制台会解析此结果并显示如下的根级别项:   现在,如果用户单击Development文件夹,控制台会将GetBucket请求发送至 OSS。此请求包括以下参数: prefix,其值为   Development/。 delimiter,其值为   /。 请求示例如下所示: GET /?prefix=Development/&delimiter=/ HTTP/1.1 Host: oss-example.oss-cn-hangzhou.aliyuncs.com Date: Fri, 24 Feb 2012 08:43:27 GMT Authorization: OSS qn6qrrqxo2oawuk53otfjbyc:DNrnx7xHk3sgysx7I8U9I9IY1vY= 作为响应,OSS 返回以指定前缀开头的key: HTTP/1.1 200 OK x-oss-request-id: 534B371674E88A4D8906008B Date: Fri, 24 Feb 2012 08:43:27 GMT Content-Type: application/xml Content-Length: 712 Connection: keep-alive Server: AliyunOSS <?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns=¡±http://doc.oss-cn-hangzhou.aliyuncs.com¡±><Name>example-company</Name><Prefix>Development/</Prefix><Marker></Marker><MaxKeys>100</MaxKeys><Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <Contents> <Key>ProjectA.docx</Key> ... </Contents> <Contents> <Key>ProjectB.docx</Key> ... </Contents></ListBucketResult> 控制台会解析此结果并显示如下的key:   教程示例 本教程示例如下所示: 创建一个存储空间example-company,然后向其中添加三个文件夹(Development、Marketing 和 Private)。 您有Anne和Leo两个用户。您希望Anne只能访问 Development文件夹而Leo则只能访问 Marketing文件夹,并且希望将 Private文件夹保持私有。在教程示例中,通过创建访问控制 (RAM) 用户(Anne和Leo)来管理访问权限,并授予他们必要的权限。 RAM 还支持创建用户组并授予适用于组中所有用户的组级别权限。这有助于更好地管理权限。在本示例中,Anne和Leo都需要一些公共权限。因此,您还要创建一个名为Staff 的组,然后将Anne和Leo添加到该组中。首先,您需要给该组分配策略授予权限。然后,将策略分配给特定用户,添加特定用户的权限。 说明  本教程示例使用example-company作为存储空间名、使用Anne和Leo作为RAM用户名并使用Staff作为组名。由于阿里云OSS要求存储空间名全局唯一,所以您需要用自己的存储空间名称替换本教程中的存储空间名。 示例准备 本示例使用阿里云主账号创建RAM用户。最初,这些用户没有任何权限。您将逐步授予这些用户执行特定 OSS 操作的权限。为了测试这些权限,您需要使用每个用户的RAM账号登录到控制台。当您作为主账号所有者逐步授予权限并作为RAM用户测试权限时,您需要每次使用不同账号进行登录和注销。您可以使用一个浏览器来执行此测试。如果您可以使用两个不同的浏览器,则该测试过程用时将会缩短:一个浏览器用于使用主账号连接到阿里云控制台,另一个浏览器用于使用RAM 账号进行连接。 要使用您的主账号登录到阿里云控制台。 RAM用户不能使用相同的链接登录。他们必须使用RAM用户登录链接。作为主账号所有者,您可以向RAM用户提供此链接。 说明  有关 RAM 的详细信息,请参见 使用 RAM 用户账号登录。 为 RAM 用户提供登录链接 使用主账号登录 RAM 控制台。 在左侧导航栏中,单击 概览。 在 RAM用户登录链接 后找到URL。您将向RAM用户提供此URL,以便其使用RAM用户名和密码登录控制台。 步骤 1:创建存储空间 在此步骤中,您可以使用主账号登录到OSS控制台、创建存储空间、将文件夹(Development、Marketing、Private)添加到存储空间中,并在每个文件夹中上传一个或两个示例文档。 使用主账号登录 OSS 控制台。 创建名为 example-company  的存储空间。 有关详细过程,请参见OSS 控制台用户指南中的创建存储空间 。 将一个文件上传到存储空间中。 本示例假设您将文件 oss-dg.pdf 上传到存储空间的根级别。您可以用不同的文件名上传自己的文件。 有关详细过程,请参见 OSS 控制台用户指南中的上传文件。 添加名为 Development、Marketing 和 Private 的三个文件夹。 有关详细过程,请参见 OSS 控制台用户指南 中的创建文件夹。 将一个或两个文件上传到每个文件夹中。 本例假设您将具有以下对象键的对象上传到存储空间中: Development/Alibaba Cloud.pdf Development/ProjectA.docx Development/ProjectB.docx Marketing/data2016.xlsx Marketing/data2016.xlsx Private/2017/images.zip Private/2017/promote.pptx oss-dg.pdf 步骤 2:创建 RAM 用户和组 在此步骤中,您使用 RAM 控制台将两个 RAM 用户 Anne 和 Leo 添加到主账号中。您还将创建一个名为 Staff 的组,然后将这两个用户添加到该组中。 说明  在此步骤中,不要分配任何授予这些用户权限的策略。在以下步骤中,您将逐步为其授予权限。 有关创建 RAM 用户的详细过程,请参见 RAM 快速入门中的创建 RAM 用户。请为每个 RAM 用户创建登录密码。 有关创建组的详细过程,请参见 RAM 用户指南中的创建组。 步骤 3:确认 RAM 用户没有任何权限 如果您使用两个浏览器,现在可以在另一个浏览器中使用其中一个 RAM 用户账号登录到控制台。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 您发现控制台中没有任何存储空间,这意味着 Anne 不具有对存储空间 example-company 的任何权限。 步骤 4:授予组级别权限 我们希望 Anne 和 Leo 都能执行以下操作: 列出主账号所拥有的所有存储空间。 为此,Anne 和 Leo 必须具有执行 oss:ListBuckets 操作的权限。 列出 example-company 存储空间中的根级别项、文件夹和对象。 为此,Anne 和 Leo 必须具有对 example-company 存储空间执行 oss:ListObjects 操作的权限。 步骤 4.1.授予列出所有存储空间的权限 在此步骤中,创建一个授予用户最低权限的策略。凭借最低权限,用户可列出主账号所拥有的所有存储空间。您还将此策略分配给 Staff 组,以便授予获得主账号拥有的存储空间列表的组权限。 使用主账号登录 RAM 控制台。 创建策略   AllowGroupToSeeBucketListInConsole。 在左侧导航窗格中,单击 策略管理,然后单击 新建授权策略。 单击 空白模板。 在 授权策略名称 字段中,输入 AllowGroupToSeeBucketListInConsole。 在 策略内容 字段中,复制并粘贴以下策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets" ], "Resource": [ "acs:oss:*:*:*" ] } ] } 说明  策略为 JSON 文档。在该策略中,Statement 是一个对象数组,每个对象使用名/值对的集合来描述权限。前面的策略描述了一个特定的权限。Effect 元素值决定是允许还是拒绝特定的权限。Action 指定访问权限的类型。在本策略中,oss:ListBuckets 是预定义的 OSS 操作,可返回经过身份验证的发送者所拥有的所有储存空间的列表。 将   AllowGroupToSeeBucketListInConsole  策略分配给 Staff 组。 有关分配策略的详细过程,请参见 RAM 快速入门 中将策略分配给 RAM 用户的将策略分配给 RAM 组。 可以将策略分配给 RAM 控制台中的 RAM 用户和组。在本例中,我们将策略分配给组,因为我们希望 Anne 和 Leo 都能够列出这些存储空间。 测试权限。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡。 此时将显示一个消息框,表明您没有相应的访问权限。 步骤 4.2.授予列出存储空间根级内容的权限 在此步骤中,您授予权限,允许所有用户列出存储空间 example-company 中的所有项目。当用户在 OSS 控制台中单击 example-company 时,能够看到存储空间中的根级别项。   使用主账号登录 RAM 控制台。 用以下策略取代分配给 Staff 组的现有策略 AllowGroupToSeeBucketListInConsole,该策略还允许 oss:ListObjects 操作。请用您的存储空间名替换策略资源中的 example-company。 有关详细过程,请参见 RAM 用户指南中授权策略的修改自定义授权策略部分。注意,您最多可对 RAM 策略进行五次修改。如果超过了五次,则需要删除该策略并创建一个新的策略,然后再次将新策略分配给 Staff 组。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets", "oss:GetBucketAcl" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": {} }, { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "" ], "oss:Delimiter": [ "/" ] } } } ] } 说明 要列出存储空间内容,用户需要调用 oss:ListObjects 操作的权限。为了确保用户仅看到根级内容,我们添加了一个条件:用户必须在请求中指定一个空前缀,也就是说,他们不能单击任何根级文件夹。我们还通过要求用户请求包含分隔符参数和值   /  来添加需要文件夹样式访问的条件。 当用户登录到 OSS 控制台时,控制台检查用户的身份是否有访问 OSS 服务的权限。要在控制台中支持存储空间操作,我们还需要添加 oss:GetBucketAcl 操作。 测试更新的权限。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡。 控制台列出所有根级别项。 单击任何文件夹或对象 oss-dg.pdf。 此时将显示一个消息框,表明您没有相应的访问权限。 组策略摘要 添加组策略的最终结果是授予 RAM 用户 Anne 和 Leo 以下最低权限: 列出主账号所拥有的所有存储空间。 查看 example-company 存储空间中的根级别项。 然而,他们可以进行的操作仍然有限。在以下部分中,我们将授予用户以下特定权限: 允许 Anne 在 Development 文件夹中获取和放入对象。 允许 Bob 在 Finance 文件夹中获取和放入对象。 对于用户特定的权限,您需要将策略分配给特定用户,而非分配给组。以下部分授予 Anne 在 Development 文件夹中操作的权限。您可以重复这些步骤,授予 Leo 在 Finance 文件夹中进行类似操作的权限。 步骤 5:授予 RAM 用户 Anne 特定权限 在此步骤中,我们向 Anne 授予额外的权限,使她可以看到 Development 文件夹的内容,并将对象放入文件夹中。 步骤 5.1.授予 RAM 用户 Anne 权限以列出 Development 文件夹内容 若要 Anne 能够列出 Development 文件夹内容,您必须为其分配策略。该策略必须能够授予其对 example-company 存储空间执行 oss:ListObjects 操作的权限,还必须包括要求用户在请求中指定前缀 Development/ 的条件。 使用主账号登录 RAM 控制台。 创建策略 AllowListBucketsIfSpecificPrefixIsIncluded,授予 RAM 用户 Anne 权限以列出 Development 文件夹内容。 在左侧导航窗格中,单击 策略管理,然后单击 新建授权策略。 单击 空白模板。 在 授权策略名称字段中,输入 AllowListBucketsIfSpecificPrefixIsIncluded。 在 策略内容 字段中,复制并粘贴以下策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } } ] } 将策略分配给 RAM 用户 Anne。 有关分配策略的详细过程,请参见 RAM 快速入门中的将策略分配给 RAM 用户 测试 Anne 的权限。 打开 RAM 用户登录链接,并用 Anne 的账号登录到 RAM 控制台。 打开 OSS 控制台,控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡,控制台列出所有根级别项。 单击 Development/ 文件夹。控制台列出文件夹中的对象。 步骤 5.2 授予 RAM 用户 Anne 在 Development 文件夹中获取和放入对象的权限。 若要 Anne 能够在 Development 文件夹中获取和放入对象,您必须授予她调用 oss:GetObject 和 oss:PutObject 操作的权限,包括用户必须在请求中指定前缀 Development/ 的条件。 使用主账号登录   RAM 控制台。 用以下策略取代您在之前步骤中创建的策略 AllowListBucketsIfSpecificPrefixIsIncluded。 有关详细过程,请参见 RAM 用户指南中授权策略的修改自定义授权策略部分。注意,您最多可对 RAM 策略进行五次修改。如果超过了五次,则需要删除该策略并创建一个新的策略,然后再次将新策略分配给 Staff 组。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } }, { "Effect": "Allow", "Action": [ "oss:GetObject", "oss:PutObject", "oss:GetObjectAcl" ], "Resource": [ "acs:oss:*:*:example-company/Development/*" ], "Condition": {} } ] } 说明  当用户登录到 OSS 控制台时,控制台检查用户的身份是否有访问 OSS 服务的权限。要在控制台中支持存储空间操作,我们还需要添加 oss:GetObjectAcl 操作。 测试更新的策略。 打开 RAM 用户登录链接,并用 Anne 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 在 OSS 控制台中,确认 Anne 现在可以在 Development 文件夹中添加对象并下载对象。 步骤 5.3 显式拒绝 RAM 用户 Anne 访问存储空间中任何其他文件夹的权限 RAM 用户 Anne 现在可以在 example-company 存储空间中列出根级内容,并将对象放入 Development 文件夹中。如果要严格限制访问权限,您可以显式拒绝 Anne 对存储空间中任何其他文件夹的访问。如果有授予 Anne 访问存储空间中任何其他文件夹的其他策略,则此显式策略将替代这些权限。 您可以将以下语句添加到 RAM 用户 Anne 的策略   AllowListBucketsIfSpecificPrefixIsIncluded,以要求 Anne 发送到 OSS 的所有请求包含前缀参数,该参数的值可以是 Development/* 或空字符串。 { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringNotLike": { "oss:Prefix": [ "Development/*", "" ] } } } 按照之前的步骤更新您为 RAM 用户 Anne 创建的策略   AllowListBucketsIfSpecificPrefixIsIncluded。复制并粘贴以下策略以替换现有策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } }, { "Effect": "Allow", "Action": [ "oss:GetObject", "oss:PutObject", "oss:GetObjectAcl" ], "Resource": [ "acs:oss:*:*:example-company/Development/*" ], "Condition": {} }, { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringNotLike": { "oss:Prefix": [ "Development/*", "" ] } } } ] } 步骤 6:授予 RAM 用户 Leo 特定权限 现在,您希望授予 Leo 访问 Marketing 文件夹的权限。请遵循之前用于向 Anne 授予权限的步骤,但应将 Development 文件夹替换为 Marketing 文件夹。有关详细过程,请参见步骤 5:授予 RAM 用户 Anne 特定权限。 步骤 7:确保 Private 文件夹安全 在本例中,您仅拥有两个用户。您在组级别授予两个用户所有所需的最小权限,只有当您真正需要单个用户级别上的权限时,才授予用户级别权限。此方法有助于最大限度地减少管理权限的工作量。随着用户数量的增加,我们希望确保不意外地授予用户对 Private 文件夹的权限。因此,我们需要添加一个显式拒绝访问 Private 文件夹的策略。显式拒绝策略会取代任何其他权限。若要确保 Private 文件夹保持私有,可以向组策略添加以下两个拒绝语句: 添加以下语句以显式拒绝对 Private 文件夹 (example-company/Private/*) 中的资源执行任何操作。 { "Effect": "Deny", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:example-company/Private/*" ], "Condition": {} } 您还要在请求指定了 Private/ prefix 时拒绝执行 ListObjects 操作的权限。在控制台中,如果 Anne 或 Leo 单击 Private 文件夹,则此策略将导致 OSS 返回错误响应。 { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": { "StringLike": { "oss:Prefix": [ "Private/" ] } } } 用包含前述拒绝语句的更新策略取代 Staff 组策略AllowGroupToSeeBucketListInConsole。在应用更新策略后,组中的任何用户都不能访问您的存储空间中的 Private 文件夹。 使用主账号登录 RAM 控制台。 用以下策略取代分配给 Staff 组的现有策略 AllowGroupToSeeBucketListInConsole。请用您的存储空间名替换策略资源中的 example-company。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets", "oss:GetBucketAcl" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": {} }, { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "" ], "oss:Delimiter": [ "/" ] } } }, { "Effect": "Deny", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:example-company/Private/*" ], "Condition": {} }, { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": { "StringLike": { "oss:Prefix": [ "Private/" ] } } } ] } 清理 要进行清理,您需要在 RAM 控制台中删除用户 Anne 和 Leo。 有关详细过程,请参见 RAM 用户指南中用户的删除RAM用户部分。 为了确保您不再因存储而继续被收取费用,您还需要删除为本示例创建的对象和存储空间。
51干警网 2019-12-02 00:38:21 0 浏览量 回答数 0

回答

把类的代码贴出来呢回复<aclass='referer'target='_blank'>@开源中国首席门卫小哥:类的代码太多,我有4个持久化类,org.hibernate.tool.hbm2ddl.SchemaUpdateexecute严重:Unknowndatabase'proman'这个错误什么意思?我数据库已经建立好了叫ssh这个Unknowndatabase'proman'什么意思?回复<aclass='referer'target='_blank'>@我爱编程zc:你hibernate配置文件里的database配置错了 <spanstyle="color:#E53333;">严重:Unknowndatabase'proman' hibernate能帮你自动建表,已经很不错了,还要指望它能自动建数据库,着实有点为难它,另外,数据库结构这些东西,还是手动来建的好我是用持久化类来进行建立数据库中的表哦数据库我已经建立了好了叫ssh数据库建好后使用Hibernate非常好,你居然用Hibernate去创建数据库我数据库已经手动建立好了,我只是用持久化类来建立表,但是报错什么意思?Unknowndatabase'proman'我的数据是叫ssh这个proman怎么来的?无知最可怕你真当Hibernate万能???估计你在配置Hibernate的时候,没有配置对,没有找到对应的库。找到库,然后运行程序,自动生成表就可以了,然后就可以访问表了回复<aclass='referer'target='_blank'>@我爱编程zc:或者Hibernate包有问题也可能最好贴出代码我们就清楚了回复<aclass='referer'target='_blank'>@我爱编程zc:你执行自动生成表的那个程序看下如果还是找不到库就一定是那里的问题最好把代码贴出来真心看不见。。。配置文件是我自己配置的应该没问题呀!数据库连接配置,数据库的名字看一下 这是你的配置你检查一下 <imgsrc="http://static.oschina.net/uploads/space/2015/1209/154543_BDws_2528735.png"alt=""> 前面运行正常这里出现没有找到对应表  <imgsrc="http://static.oschina.net/uploads/space/2015/1209/154650_M3FX_2528735.png"alt=""> 这里出错,然后Hibernate默认连接,后面是由这一个错误而引发的,看一下Hibernat配置,特别是<spanstyle="font-size:10px;">*<spanstyle="font-family:Arial;font-size:10px;line-height:27px;background-color:#FFFFFF;">.hbm.xml文件和程序 <imgsrc="http://static.oschina.net/uploads/space/2015/1209/154813_0bGy_2528735.png"alt="">回复<aclass='referer'target='_blank'>@我爱编程zc:客气了回复<aclass='referer'target='_blank'>@水门-kay:谢谢你的帮助!回复<aclass='referer'target='_blank'>@我爱编程zc:额我们马上要开会啊。。。我的邮箱1132892414@qq.com你发给我吧回复<aclass='referer'target='_blank'>@水门-kay:帮忙看下我的.hbm.xml文件?谢谢大神,对了之前的测试正确能够在ssh数据库中产生user表回复<aclass='referer'target='_blank'>@我爱编程zc:至于为什么显示Unknowdatabase'proman'感觉是由错误引发的应该不是配置的问题(⊙﹏⊙)b我也不清楚你的操作呀
爱吃鱼的程序员 2020-06-10 10:14:28 0 浏览量 回答数 0

问题

深入理解Magento - 第二章 - Magento请求分发与控制器 400 请求报错 

深入理解Magento 作者: Alan Storm 翻译: zhlmmc 第二章 - Magento请求分发与控制器 Model-View-Controller (MVC) ,模型-视图-控制器...
kun坤 2020-05-28 16:31:47 81 浏览量 回答数 0

问题

请教spring 3+mybatis整合出错问题:Invalid bound s?400报错

请教spring 3+mybatis整合出错问题:Invalid bound statement (not found):? 400 报错 spring 3+mybatis3+spring secu...
爱吃鱼的程序员 2020-05-30 23:56:27 0 浏览量 回答数 1

回答

官网 https://swagger.io Swagger自动化文档工具 Swagger是一个完整的API生态,工具,规范,代码生成。 用于描述,生成,使用和可视化RESTful Web服务。Swagger API project 2011 Tony Tam创立 最早Java版。SmartBear Software公司支持,Apache License 2.0。OpenAPI Spec。Swagger and OAS。Swagger 2 to OpenAPI 3。捐赠给linux基金会。行业标准规范。Swagger Tools一套工具:设计、开发、测试、监控、治理。 Spring REST Docs Spring REST Docs帮助自动化生成RESTful服务的文档。 使用Asciidoctor编写的手写文档Spring REST Docs为RESTful服务生成准确且可读的文档。将手写文档与使用Spring测试生成的文档片段相结合。不受Swagger等工具生成的文档的限制。它可以生成准确,简洁和结构良好的API文档。Spring REST Docs支持测试驱动Test Driven。Spring REST Docs支持Spring MVC Test框架,Spring WebFlux的 WebTestClient或REST Assured 3测试驱动。Spring Boot 提供了注解@AutoConfigureRestDocs替代SpringFox Swagger 优点 手写文档与使用Spring Test框架生成的文档片段结合。 curl and http request snippets are generated。easy to package documentation in projects jar file。easy to add extra information to the snippets。supports both JSON and XML。 MockMvc MockMvc是Spring MVC Test工具类,支持Assert和Chain。 @Mock创建模拟对象,Mock。@InjectMocks会自动将mock依赖注入测试对象。MockitoAnnotations.initMocks(this)初始化。MockMvcBuilders.standaloneSetup(..).build()通过注册一个或多 个@Controller实例并以编程方式配置Spring MVC基础结构来构建 MockMvc实例。@Test 标注测试方法。@WebMvcTest注解用于Spring MVC测试。 它禁用完全自动配置,而只应用与MVC测试相关的配置。WebMvcTest注解也自动配置MockMvc实例。 Asciidoctor插件步骤 pom.xml添加Asciidoctor插件 添加对spring-restdocs-mockmvc的依赖配置属性asciidocs输出位置sourceDirectory配置测试任务task输出位置outputDirectory配置asciidoctor tasksnippets定义snippets输出位置使task依赖于test任务,以便在创建文档之前运行测试将snippets配置为输入。将在此目录下创建所有代码段 REST Assured Rest-Assured 由 Java 实现的 REST API 测试框架 在Java中测试和验证REST服务比在Ruby和Groovy等动态语言中更难。REST Assured简化REST API测试。专为测试 REST API 而设计的 DSLJava DSL,用于轻松测试REST服务REST Assured支持任何HTTP方法,但明确支持POST,GET,PUT,DELETE, OPTIONS,PATCH和HEAD,并包括指定和验证例如 parameters, headers, cookies body 。自动化测试http://rest-assured.io/ Spring Auto Rest Docs Spring REST Docs 最低要求 Java 8 Spring Framework 5 (5.0.2 or later)此外, the spring-restdocs-restassured要求 :REST Assured 3.0 Spring Rest Docs Demo Spring Rest Docs实战 Asciidocs Maven Plugins Spring REST Docs可以在线方便的调试自己的API,但是没有 Swagger 使用方便,这边就简单介绍下,重点还是讲实战Swagger。 Spring Boot 2.0 实战Swagger 引用Swagger的包,需要自己做一些参数化的配置,简单的可以在配置文件进行,复杂一些配置需要在代码里面进行。生成的调试方式也比较简单,生成的网页里面在详细的检索描述性,可以在线的发送get、Post等经典请求格式,很方便的去调接口,对于前后端分离的架构来说是很方便。 页面打开两种方式: http://localhost:8081/swagger-ui.html /v2/api-docs Swagger UI /swagger-ui.html 接口文档的版本可以不断的变化,也可以在后台进行配置 Swagger-core 注解 在开发过程中,默认的话什么都不加的话,实际解析的信息如说控制器或者类别的基本信息。如果希望对内加一些描述信息。对原接口加原表述信息的话,可以加进来如传输的数据类型,加个model的 API model加个说明,模组里面字段你可以加property这个说明。操作具体方法的话可以operation,参数的话有pyramid的说明,应答消息和请求消息的话也可以加 response,这种相对的这些注解说明都可以了。它会自动的把这些信息提取出来,生成放到Swagger在线帮助文档里。 Spring Boot 2.0 Rest API注解 应答消息401、404、403等消息可以自己定制,如整合API的类型的话,可以加入淘宝用户的API接口等,根据自己的需求进行添加。 接口也可以分类,目前是这里简单做了几个分类:订单接口,用户接口,并可以在里面进行测试,方便在线检查,并视图形式,反馈各个消息类型的结果。 测试有错注意点: 需要使用RestController,不要是用Controller 出现order repository 问题,是没有数据的原因 Swagger功能非常强大,也方便调试开发,尤其是前后端分离的架构。
1358896759097293 2021-05-02 23:44:57 0 浏览量 回答数 0

回答

初识 MyBatis MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。 MyBatis 的特点 那么 MyBatis 具有什么特点呢?或许我们可以从如下几个方面来描述 MyBatis 中的 SQL 语句和主要业务代码分离,我们一般会把 MyBatis 中的 SQL 语句统一放在 XML 配置文件中,便于统一维护。 解除 SQL 与程序代码的耦合,通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。SQL 和代码的分离,提高了可维护性。 MyBatis 比较简单和轻量 本身就很小且简单。没有任何第三方依赖,只要通过配置 jar 包,或者如果你使用 Maven 项目的话只需要配置 Maven 以来就可以。易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。 屏蔽样板代码 MyBatis 回屏蔽原始的 JDBC 样板代码,让你把更多的精力专注于 SQL 的书写和属性-字段映射上。 编写原生 SQL,支持多表关联 MyBatis 最主要的特点就是你可以手动编写 SQL 语句,能够支持多表关联查询。 提供映射标签,支持对象与数据库的 ORM 字段关系映射 ORM 是什么?对象关系映射(Object Relational Mapping,简称ORM) ,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 提供 XML 标签,支持编写动态 SQL。 你可以使用 MyBatis XML 标签,起到 SQL 模版的效果,减少繁杂的 SQL 语句,便于维护。 MyBatis 整体架构 MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -> XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。 接口层 在不与Spring 集成的情况下,使用 MyBatis 执行数据库的操作主要如下: InputStream is = Resources.getResourceAsStream("myBatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); sqlSession = factory.openSession(); 其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是MyBatis 中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。 数据处理层 配置解析 在 Mybatis 初始化过程中,会加载 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configration 对象中。之后,根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后,可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。 SQL 解析与 scripting 模块 Mybatis 实现的动态 SQL 语句,几乎可以编写出所有满足需要的 SQL。 Mybatis 中 scripting 模块会根据用户传入的参数,解析映射文件中定义的动态 SQL 节点,形成数据库能执行的SQL 语句。 SQL 执行 SQL 语句的执行涉及多个组件,包括 MyBatis 的四大核心,它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的执行过程可以用下面这幅图来表示 MyBatis 层级结构各个组件的介绍(这里只是简单介绍,具体介绍在后面): SqlSession: ,它是 MyBatis 核心 API,主要用来执行命令,获取映射,管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。Executor :执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成以及查询缓存的维护。StatementHandler : 封装了JDBC Statement 操作,负责对 JDBC Statement 的操作,如设置参数、将Statement 结果集转换成 List 集合。ParameterHandler : 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。ResultSetHandler : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。TypeHandler : 用于 Java 类型和 JDBC 类型之间的转换。MappedStatement : 动态 SQL 的封装SqlSource : 表示从 XML 文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的 SQL。Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。 基础支持层 反射模块 Mybatis 中的反射模块,对 Java 反射进行了很好的封装,提供了简易的 API,方便上层调用,并且对反射操作进行了一系列的优化,比如,缓存了类的 元数据(MetaClass)和对象的元数据(MetaObject),提高了反射操作的性能。 类型转换模块 Mybatis 的别名机制,能够简化配置文件,该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型的转换。在 SQL 语句绑定参数时,会将数据由 Java 类型转换成 JDBC 类型;在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。 日志模块 在 Java 中,有很多优秀的日志框架,如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架。 资源加载模块 该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能。 解析器模块 该模块有两个主要功能:一个是封装了 XPath,为 Mybatis 初始化时解析 mybatis-config.xml配置文件以及映射配置文件提供支持;另一个为处理动态 SQL 语句中的占位符提供支持。 数据源模块 Mybatis 自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供ORM 框架以及整个应用的性能都是非常重要的。 事务管理模块 一般地,Mybatis 与 Spring 框架集成,由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现。 缓存模块 Mybatis 中有一级缓存和二级缓存,这两级缓存都依赖于缓存模块中的实现。但是需要注意,这两级缓存与Mybatis 以及整个应用是运行在同一个 JVM 中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品。 Binding 模块 在调用 SqlSession 相应方法执行数据库操作时,需要制定映射文件中定义的 SQL 节点,如果 SQL 中出现了拼写错误,那就只能在运行时才能发现。为了能尽早发现这种错误,Mybatis 通过 Binding 模块将用户自定义的Mapper 接口与映射文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。注意,在开发中,我们只是创建了 Mapper 接口,而并没有编写实现类,这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象。 MyBatis 核心组件 在认识了 MyBatis 并了解其基础架构之后,下面我们来看一下 MyBatis 的核心组件,就是这些组件实现了从 SQL 语句到映射到 JDBC 再到数据库字段之间的转换,执行 SQL 语句并输出结果集。首先来认识 MyBatis 的第一个核心组件 SqlSessionFactory 对于任何框架而言,在使用该框架之前都要经历过一系列的初始化流程,MyBatis 也不例外。MyBatis 的初始化流程如下 String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlSessionFactory.openSession(); 上述流程中比较重要的一个对象就是SqlSessionFactory,SqlSessionFactory 是 MyBatis 框架中的一个接口,它主要负责的是 MyBatis 框架初始化操作 为开发人员提供SqlSession 对象 SqlSessionFactory 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory 类 DefaultSqlSessionFactory : SqlSessionFactory 的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。 SqlSessionManager : 已被废弃,原因大概是: SqlSessionManager 中需要维护一个自己的线程池,而使用MyBatis 更多的是要与 Spring 进行集成,并不会单独使用,所以维护自己的 ThreadLocal 并没有什么意义,所以 SqlSessionManager 已经不再使用。 ####SqlSessionFactory 的执行流程 下面来对 SqlSessionFactory 的执行流程来做一个分析 首先第一步是 SqlSessionFactory 的创建 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 1 从这行代码入手,首先创建了一个 SqlSessionFactoryBuilder 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂 然后调用 SqlSessionFactoryBuilder 中的 build 方法传递一个InputStream 输入流,Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environment、properties属性创建一个XMLConfigBuilder对象。SqlSessionFactoryBuilder 对象调用XMLConfigBuilder 的parse()方法,流程如下。 XMLConfigBuilder 会解析/configuration标签,configuration 是 MyBatis 中最重要的一个标签,下面流程会介绍 Configuration 标签。 MyBatis 默认使用 XPath 来解析标签,关于 XPath 的使用,参见 https://www.w3school.com.cn/xpath/index.asp 在 parseConfiguration 方法中,会对各个在 /configuration 中的标签进行解析 重要配置 说一下这些标签都是什么意思吧 properties,外部属性,这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。 <properties> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="root" /> </properties> 一般用来给 environment 标签中的 dataSource 赋值 <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> 还可以通过外部属性进行配置,但是我们这篇文章以原理为主,不会介绍太多应用层面的操作。 settings ,MyBatis 中极其重要的配置,它们会改变 MyBatis 的运行时行为。 settings 中配置有很多,具体可以参考 https://mybatis.org/mybatis-3/zh/configuration.html#settings 详细了解。这里介绍几个平常使用过程中比较重要的配置 一般使用如下配置 <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> </settings> typeAliases,类型别名,类型别名是为 Java 类型设置的一个名字。 它只和 XML 配置有关。 <typeAliases> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases> 当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。 typeHandlers,类型处理器,无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。 在 org.apache.ibatis.type 包下有很多已经实现好的 TypeHandler,可以参考如下 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很方便的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。 objectFactory,对象工厂,MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。 public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List constructorArgTypes, List constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } public boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); } } 然后需要在 XML 中配置此对象工厂 <objectFactory type="org.mybatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/> </objectFactory> plugins,插件开发,插件开发是 MyBatis 设计人员给开发人员留给自行开发的接口,MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。MyBatis 允许使用插件来拦截的方法调用包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler 接口,这几个接口也是 MyBatis 中非常重要的接口,我们下面会详细介绍这几个接口。 environments,MyBatis 环境配置,MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射。 这里注意一点,虽然 environments 可以指定多个环境,但是 SqlSessionFactory 只能有一个,为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties); databaseIdProvider ,数据库厂商标示,MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 <databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider> mappers,映射器,这是告诉 MyBatis 去哪里找到这些 SQL 语句,mappers 映射配置有四种方式 上面的一个个属性都对应着一个解析方法,都是使用 XPath 把标签进行解析,解析完成后返回一个 DefaultSqlSessionFactory 对象,它是 SqlSessionFactory 的默认实现类。这就是 SqlSessionFactoryBuilder 的初始化流程,通过流程我们可以看到,初始化流程就是对一个个 /configuration 标签下子标签的解析过程。 SqlSession 在 MyBatis 初始化流程结束,也就是 SqlSessionFactoryBuilder -> SqlSessionFactory 的获取流程后,我们就可以通过 SqlSessionFactory 对象得到 SqlSession 然后执行 SQL 语句了。具体来看一下这个过程‘ 在 SqlSessionFactory.openSession 过程中我们可以看到,会调用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法,这个方法主要创建了两个与我们分析执行流程重要的对象,一个是 Executor 执行器对象,一个是 SqlSession 对象。执行器我们下面会说,现在来说一下 SqlSession 对象 SqlSession 对象是 MyBatis 中最重要的一个对象,这个接口能够让你执行命令,获取映射,管理事务。SqlSession 中定义了一系列模版方法,让你能够执行简单的 CRUD 操作,也可以通过 getMapper 获取 Mapper 层,执行自定义 SQL 语句,因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话,涉及到事务操作,所以还会有 commit、 rollback、close 等方法。这也是模版设计模式的一种应用。 MapperProxy MapperProxy 是 Mapper 映射 SQL 语句的关键对象,我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy 来和对应的 SQL 语句进行绑定的。下面我们就来解释一下绑定过程 这就是 MyBatis 的核心绑定流程,我们可以看到 SqlSession 首先调用 getMapper 方法,我们刚才说到 SqlSession 是大哥级别的人物,只定义标准(有一句话是怎么说的来着,一流的企业做标准,二流的企业做品牌,三流的企业做产品)。 SqlSession 不愿意做的事情交给 Configuration 这个手下去做,但是 Configuration 也是有小弟的,它不愿意做的事情直接甩给小弟去做,这个小弟是谁呢?它就是 MapperRegistry,马上就到核心部分了。MapperRegistry 相当于项目经理,项目经理只从大面上把握项目进度,不需要知道手下的小弟是如何工作的,把任务完成了就好。最终真正干活的还是 MapperProxyFactory。看到这段代码 Proxy.newProxyInstance ,你是不是有一种恍然大悟的感觉,如果你没有的话,建议查阅一下动态代理的文章,这里推荐一篇 (https://www.jianshu.com/p/95970b089360) 也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。 通过动态代理,我们就可以方便的在 Dao 层或者 Mapper 层定义接口,实现自定义的增删改查操作了。那么具体的执行过程是怎么样呢?上面只是绑定过程,别着急,下面就来探讨一下 SQL 语句的执行过程。 MapperProxyFactory 会生成代理对象,这个对象就是 MapperProxy,最终会调用到 mapperMethod.execute 方法,execute 方法比较长,其实逻辑比较简单,就是判断是 插入、更新、删除 还是 查询 语句,其中如果是查询的话,还会判断返回值的类型,我们可以点进去看一下都是怎么设计的。 很多代码其实可以忽略,只看我标出来的重点就好了,我们可以看到,不管你前面经过多少道关卡处理,最终都逃不过 SqlSession 这个老大制定的标准。 我们以 selectList 为例,来看一下下面的执行过程。 这是 DefaultSqlSession 中 selectList 的代码,我们可以看到出现了 executor,这是什么呢?我们下面来解释。 Executor 还记得我们之前的流程中提到了 Executor(执行器) 这个概念吗?我们来回顾一下它第一次出现的位置。 由 Configuration 对象创建了一个 Executor 对象,这个 Executor 是干嘛的呢?下面我们就来认识一下 Executor 的继承结构 每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。 也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。 首先先从 Executor 的继承体系来认识一下 如上图所示,位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutor和 CachingExecutor。 BaseExecutor 是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配 的体现,是Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。 SimpleExecutor : 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象) ReuseExecutor : 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement作用域是同一个 SqlSession。 BatchExecutor : 批处理执行器,用于将多个 SQL 一次性输出到数据库 CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。 Executor 的创建和选择 我们上面提到 Executor 是由 Configuration 创建的,Configuration 会根据执行器的类型创建,如下 这一步就是执行器的创建过程,根据传入的 ExecutorType 类型来判断是哪种执行器,如果不指定 ExecutorType ,默认创建的是简单执行器。它的赋值可以通过两个地方进行赋值: 可以通过 标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor <settings> <!--取值范围 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="SIMPLE"/> </settings> 另外一种直接通过Java对方法赋值的方式 session = factory.openSession(ExecutorType.BATCH); Executor 的具体执行过程 Executor 中的大部分方法的调用链其实是差不多的,下面是深入源码分析执行过程,如果你没有时间或者暂时不想深入研究的话,给你下面的执行流程图作为参考。 我们紧跟着上面的 selectList 继续分析,它会调用到 executor.query 方法。 当有一个查询请求访问的时候,首先会经过 Executor 的实现类 CachingExecutor ,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取。 上面这段代码是从 selectList -> 从缓存中 query 的具体过程。可能你看到这里有些觉得类都是什么东西,我想鼓励你一下,把握重点,不用每段代码都看,从找到 SQL 的调用链路,其他代码想看的时候在看,看源码就是很容易发蒙,容易烦躁,但是切记一点,把握重点。 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler StatementHandler 的继承结构 有没有感觉和 Executor 的继承体系很相似呢?最顶级接口是四大组件对象,分别有两个实现类 BaseStatementHandler 和 RoutingStatementHandler,BaseStatementHandler 有三个实现类, 他们分别是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。 RoutingStatementHandler : RoutingStatementHandler 并没有对 Statement 对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。 BaseStatementHandler : 是 StatementHandler 接口的另一个实现类,它本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类 SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句。CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程。 StatementHandler 的创建和源码分析 我们继续来分析上面 query 的调用链路,StatementHandler 的创建过程如下 MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建。我们以预处理 StatementHandler 为例来讲解一下 执行器不仅掌管着 StatementHandler 的创建,还掌管着创建 Statement 对象,设置参数等,在创建完 PreparedStatement 之后,我们需要对参数进行处理了。 如 如果用一副图来表示一下这个执行流程的话我想是这样 这里我们先暂停一下,来认识一下第三个核心组件 ParameterHandler ParameterHandler - ParameterHandler 介绍 ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法 ParameterHandler 只有一个实现类 DefaultParameterHandler , 它实现了这两个方法。 getParameterObject: 用于读取参数setParameters: 用于对 PreparedStatement 的参数赋值ParameterHandler 的解析过程 上面我们讨论过了 ParameterHandler 的创建过程,下面我们继续上面 parameterSize 流程 这就是具体参数的解析过程了,下面我们来描述一下 下面用一个流程图表示一下 ParameterHandler 的解析过程,以简单执行器为例 我们在完成 ParameterHandler 对 SQL 参数的预处理后,回到 SimpleExecutor 中的 doQuery 方法 上面又引出来了一个重要的组件那就是 ResultSetHandler,下面我们来认识一下这个组件 ResultSetHandler - ResultSetHandler 简介 ResultSetHandler 也是一个非常简单的接口 ResultSetHandler 是一个接口,它只有一个默认的实现类,像是 ParameterHandler 一样,它的默认实现类是DefaultResultSetHandler ResultSetHandler 解析过程 MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,DefaultResultSetHandler 主要负责处理两件事 处理 Statement 执行后产生的结果集,生成结果列表 处理存储过程执行后的输出参数 按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。 其中涉及的主要对象有: ResultSetWrapper : 结果集的包装器,主要针对结果集进行的一层包装,它的主要属性有 ResultSet : Java JDBC ResultSet 接口表示数据库查询的结果。 有关查询的文本显示了如何将查询结果作为java.sql.ResultSet 返回。 然后迭代此ResultSet以检查结果。 TypeHandlerRegistry: 类型注册器,TypeHandlerRegistry 在初始化的时候会把所有的 Java类型和类型转换器进行注册。 ColumnNames: 字段的名称,也就是查询操作需要返回的字段名称 ClassNames: 字段的类型名称,也就是 ColumnNames 每个字段名称的类型 JdbcTypes: JDBC 的类型,也就是 java.sql.Types 类型 ResultMap: 负责处理更复杂的映射关系 在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。 内容转载自:CSDN博主:cxuann 原文链接:https://blog.csdn.net/qq_36894974/article/details/104132876?depth_1-utm_source=distribute.pc_feed.none-task&request_id=&utm_source=distribute.pc_feed.none-task
问问小秘 2020-03-05 15:44:27 0 浏览量 回答数 0

回答

Android平台进行数据存储的五大方式,分别如下: 1 使用SharedPreferences存储数据 2 文件存储数据 3 SQLite数据库存储数据 4 使用ContentProvider存储数据 5 网络存储数据 下面详细讲解这五种方式的特点 第一种: 使用SharedPreferences存储数据 适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口 令密码等 核心原理:保存基于XML文件存储的key-value键值对数据,通常用来存储一些简单的配置信息。通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data/<package name>/shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。 SharedPreferences本身是一 个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,第二个参数具体如下: Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。 Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。 Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序读,写 Editor有如下主要重要方法: SharedPreferences.Editor clear():清空SharedPreferences里所有数据 SharedPreferences.Editor putXxx(String key , xxx value): 向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据 SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项 boolean commit(): 当Editor编辑完成后,使用该方法提交修改 实际案例:运行界面如下 这里只提供了两个按钮和一个输入文本框,布局简单,故在此不给出界面布局文件了,程序核心代码如下: 、class ViewOcl implements View.OnClickListener{ @Override public void onClick(View v) { switch(v.getId()){ case R.id.btnSet: //步骤1:获取输入值 String code = txtCode.getText().toString().trim(); //步骤2-1:创建一个SharedPreferences.Editor接口对象,lock表示要写入的XML文件名,MODE_WORLD_WRITEABLE写操作 SharedPreferences.Editor editor = getSharedPreferences("lock", MODE_WORLD_WRITEABLE).edit(); //步骤2-2:将获取过来的值放入文件 editor.putString("code", code); //步骤3:提交 editor.commit(); Toast.makeText(getApplicationContext(), "口令设置成功", Toast.LENGTH_LONG).show(); break; case R.id.btnGet: //步骤1:创建一个SharedPreferences接口对象 SharedPreferences read = getSharedPreferences("lock", MODE_WORLD_READABLE); //步骤2:获取文件中的值 String value = read.getString("code", ""); Toast.makeText(getApplicationContext(), "口令为:"+value, Toast.LENGTH_LONG).show(); break; } } } 、读写其他应用的SharedPreferences: 步骤如下 1、在创建SharedPreferences时,指定MODE_WORLD_READABLE模式,表明该SharedPreferences数据可以被其他程序读取 2、创建其他应用程序对应的Context: Context pvCount = createPackageContext("com.tony.app", Context.CONTEXT_IGNORE_SECURITY);这里的com.tony.app就是其他程序的包名 3、使用其他程序的Context获取对应的SharedPreferences SharedPreferences read = pvCount.getSharedPreferences("lock", Context.MODE_WORLD_READABLE); 4、如果是写入数据,使用Editor接口即可,所有其他操作均和前面一致。 SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其职能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。 第二种: 文件存储数据 核心原理: Context提供了两个方法来打开数据文件里的文件IO流 FileInputStream openFileInput(String name); FileOutputStream(String name , int mode),这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。具体有以下值可选: MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可 以使用Context.MODE_APPEND MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。 MODE_WORLD_READABLE:表示当前文件可以被其他应用读取; MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。 除此之外,Context还提供了如下几个重要的方法: getDir(String name , int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录 File getFilesDir():获取该应用程序的数据文件夹得绝对路径 String[] fileList():返回该应用数据文件夹的全部文件 public String read() { try { FileInputStream inStream = this.openFileInput("message.txt"); byte[] buffer = new byte[1024]; int hasRead = 0; StringBuilder sb = new StringBuilder(); while ((hasRead = inStream.read(buffer)) != -1) { sb.append(new String(buffer, 0, hasRead)); } inStream.close(); return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } public void write(String msg){ // 步骤1:获取输入值 if(msg == null) return; try { // 步骤2:创建一个FileOutputStream对象,MODE_APPEND追加模式 FileOutputStream fos = openFileOutput("message.txt", MODE_APPEND); // 步骤3:将获取过来的值放入文件 fos.write(msg.getBytes()); // 步骤4:关闭数据流 fos.close(); } catch (Exception e) { e.printStackTrace(); } } openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data//files目录,如: /data/data/cn.tony.app/files/message.txt, 下面讲解某些特殊文件读写需要注意的地方: 读写sdcard上的文件 其中读写步骤按如下进行: 1、调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,且应用程序具有读写SD卡的权限,如下代码将返回true Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 2、调用Environment.getExternalStorageDirectory()方法来获取外部存储器,也就是SD卡的目录,或者使用"/mnt/sdcard/"目录 3、使用IO流操作SD卡上的文件 注意点:手机应该已插入SD卡,对于模拟器而言,可通过mksdcard命令来创建虚拟存储卡 必须在AndroidManifest.xml上配置读写SD卡的权限 // 文件写操作函数 private void write(String content) { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } PrintStream out = null; // 打印流对象用于输出 try { out = new PrintStream(new FileOutputStream(file, true)); // 追加文件 out.println(content); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { out.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "保存失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } } // 文件读操作函数 private String read() { if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); // 定义File类对象 if (!file.getParentFile().exists()) { // 父文件夹不存在 file.getParentFile().mkdirs(); // 创建文件夹 } Scanner scan = null; // 扫描输入 StringBuilder sb = new StringBuilder(); try { scan = new Scanner(new FileInputStream(file)); // 实例化Scanner while (scan.hasNext()) { // 循环读取 sb.append(scan.next() + "\n"); // 设置文本 } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { if (scan != null) { scan.close(); // 关闭打印流 } } } else { // SDCard不存在,使用Toast提示用户 Toast.makeText(this, "读取失败,SD卡不存在!", Toast.LENGTH_LONG).show(); } return null; } 复制代码 第三种:SQLite存储数据 SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。现在的主流移动设备像Android、iPhone等都使用SQLite作为复杂数据的存储引擎,在我们为移动设备开发应用程序时,也许就要使用到SQLite来存储我们大量的数据,所以我们就需要掌握移动设备上的SQLite开发技巧 SQLiteDatabase类为我们提供了很多种方法,上面的代码中基本上囊括了大部分的数据库操作;对于添加、更新和删除来说,我们都可以使用 1 db.executeSQL(String sql); 2 db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集 除了统一的形式之外,他们还有各自的操作方法: 1 db.insert(String table, String nullColumnHack, ContentValues values); 2 db.update(String table, Contentvalues values, String whereClause, String whereArgs); 3 db.delete(String table, String whereClause, String whereArgs);以上三个方法的第一个参数都是表示要操作的表名;insert中的第二个参数表示如果插入的数据每一列都为空的话,需要指定此行中某一列的名称,系统将此列设置为NULL,不至于出现错误;insert中的第三个参数是ContentValues类型的变量,是键值对组成的Map,key代表列名,value代表该列要插入的值;update的第二个参数也很类似,只不过它是更新该字段key为最新的value值,第三个参数whereClause表示WHERE表达式,比如“age > ? and age < ?”等,最后的whereArgs参数是占位符的实际参数值;delete方法的参数也是一样 下面给出demo 数据的添加 1.使用insert方法 复制代码1 ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据2 cv.put("title","you are beautiful");//添加title3 cv.put("weather","sun"); //添加weather4 cv.put("context","xxxx"); //添加context5 String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")6 .format(new Date());7 cv.put("publish ",publish); //添加publish8 db.insert("diary",null,cv);//执行插入操作复制代码2.使用execSQL方式来实现 String sql = "insert into user(username,password) values ('Jack Johnson','iLovePopMuisc');//插入操作的SQL语句db.execSQL(sql);//执行SQL语句数据的删除 同样有2种方式可以实现 String whereClause = "username=?";//删除的条件String[] whereArgs = {"Jack Johnson"};//删除的条件参数db.delete("user",whereClause,whereArgs);//执行删除使用execSQL方式的实现 String sql = "delete from user where username='Jack Johnson'";//删除操作的SQL语句db.execSQL(sql);//执行删除操作数据修改 同上,仍是2种方式 ContentValues cv = new ContentValues();//实例化ContentValuescv.put("password","iHatePopMusic");//添加要更改的字段及内容String whereClause = "username=?";//修改条件String[] whereArgs = {"Jack Johnson"};//修改条件的参数db.update("user",cv,whereClause,whereArgs);//执行修改使用execSQL方式的实现 String sql = "update user set password = 'iHatePopMusic' where username='Jack Johnson'";//修改的SQL语句db.execSQL(sql);//执行修改数据查询 下面来说说查询操作。查询操作相对于上面的几种操作要复杂些,因为我们经常要面对着各种各样的查询条件,所以系统也考虑到这种复杂性,为我们提供了较为丰富的查询形式: 1 db.rawQuery(String sql, String[] selectionArgs); 2 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy); 3 db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit); 4 db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit); 上面几种都是常用的查询方法,第一种最为简单,将所有的SQL语句都组织到一个字符串中,使用占位符代替实际参数,selectionArgs就是占位符实际参数集; 各参数说明: table:表名称colums:表示要查询的列所有名称集selection:表示WHERE之后的条件语句,可以使用占位符selectionArgs:条件语句的参数数组groupBy:指定分组的列名having:指定分组条件,配合groupBy使用orderBy:y指定排序的列名limit:指定分页参数distinct:指定“true”或“false”表示要不要过滤重复值Cursor:返回值,相当于结果集ResultSet最后,他们同时返回一个Cursor对象,代表数据集的游标,有点类似于JavaSE中的ResultSet。下面是Cursor对象的常用方法: 复制代码 1 c.move(int offset); //以当前位置为参考,移动到指定行 2 c.moveToFirst(); //移动到第一行 3 c.moveToLast(); //移动到最后一行 4 c.moveToPosition(int position); //移动到指定行 5 c.moveToPrevious(); //移动到前一行 6 c.moveToNext(); //移动到下一行 7 c.isFirst(); //是否指向第一条 8 c.isLast(); //是否指向最后一条 9 c.isBeforeFirst(); //是否指向第一条之前 10 c.isAfterLast(); //是否指向最后一条之后 11 c.isNull(int columnIndex); //指定列是否为空(列基数为0) 12 c.isClosed(); //游标是否已关闭 13 c.getCount(); //总数据项数 14 c.getPosition(); //返回当前游标所指向的行数 15 c.getColumnIndex(String columnName);//返回某列名对应的列索引值 16 c.getString(int columnIndex); //返回当前行指定列的值 复制代码实现代码 复制代码String[] params = {12345,123456};Cursor cursor = db.query("user",columns,"ID=?",params,null,null,null);//查询并获得游标if(cursor.moveToFirst()){//判断游标是否为空 for(int i=0;i<cursor.getCount();i++){ cursor.move(i);//移动到指定记录 String username = cursor.getString(cursor.getColumnIndex("username"); String password = cursor.getString(cursor.getColumnIndex("password")); } }复制代码通过rawQuery实现的带参数查询 复制代码Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");//Cursor c = db.rawQuery("s name, inventory FROM mytable where ID=?",new Stirng[]{"123456"}); result.moveToFirst(); while (!result.isAfterLast()) { int id=result.getInt(0); String name=result.getString(1); int inventory=result.getInt(2); // do something useful with these result.moveToNext(); } result.close();复制代码 在上面的代码示例中,已经用到了这几个常用方法中的一些,关于更多的信息,大家可以参考官方文档中的说明。 最后当我们完成了对数据库的操作后,记得调用SQLiteDatabase的close()方法释放数据库连接,否则容易出现SQLiteException。 上面就是SQLite的基本应用,但在实际开发中,为了能够更好的管理和维护数据库,我们会封装一个继承自SQLiteOpenHelper类的数据库操作类,然后以这个类为基础,再封装我们的业务逻辑方法。 这里直接使用案例讲解:下面是案例demo的界面 SQLiteOpenHelper类介绍 SQLiteOpenHelper是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。一般是建立一个类继承它,并实现它的onCreate和onUpgrade方法。 方法名 方法描述SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version) 构造方法,其中 context 程序上下文环境 即:XXXActivity.this; name :数据库名字; factory:游标工厂,默认为null,即为使用默认工厂; version 数据库版本号 onCreate(SQLiteDatabase db) 创建数据库时调用onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion) 版本更新时调用getReadableDatabase() 创建或打开一个只读数据库getWritableDatabase() 创建或打开一个读写数据库首先创建数据库类 复制代码 1 import android.content.Context; 2 import android.database.sqlite.SQLiteDatabase; 3 import android.database.sqlite.SQLiteDatabase.CursorFactory; 4 import android.database.sqlite.SQLiteOpenHelper; 5 6 public class SqliteDBHelper extends SQLiteOpenHelper { 7 8 // 步骤1:设置常数参量 9 private static final String DATABASE_NAME = "diary_db";10 private static final int VERSION = 1;11 private static final String TABLE_NAME = "diary";12 13 // 步骤2:重载构造方法14 public SqliteDBHelper(Context context) {15 super(context, DATABASE_NAME, null, VERSION);16 }17 18 /*19 * 参数介绍:context 程序上下文环境 即:XXXActivity.this 20 * name 数据库名字 21 * factory 接收数据,一般情况为null22 * version 数据库版本号23 */24 public SqliteDBHelper(Context context, String name, CursorFactory factory,25 int version) {26 super(context, name, factory, version);27 }28 //数据库第一次被创建时,onCreate()会被调用29 @Override30 public void onCreate(SQLiteDatabase db) {31 // 步骤3:数据库表的创建32 String strSQL = "create table "33 + TABLE_NAME34 + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";35 //步骤4:使用参数db,创建对象36 db.execSQL(strSQL);37 }38 //数据库版本变化时,会调用onUpgrade()39 @Override40 public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {41 42 }43 }复制代码正如上面所述,数据库第一次创建时onCreate方法会被调用,我们可以执行创建表的语句,当系统发现版本变化之后,会调用onUpgrade方法,我们可以执行修改表结构等语句。 我们需要一个Dao,来封装我们所有的业务方法,代码如下: 复制代码 1 import android.content.Context; 2 import android.database.Cursor; 3 import android.database.sqlite.SQLiteDatabase; 4 5 import com.chinasoft.dbhelper.SqliteDBHelper; 6 7 public class DiaryDao { 8 9 private SqliteDBHelper sqliteDBHelper;10 private SQLiteDatabase db;11 12 // 重写构造方法13 public DiaryDao(Context context) {14 this.sqliteDBHelper = new SqliteDBHelper(context);15 db = sqliteDBHelper.getWritableDatabase();16 }17 18 // 读操作19 public String execQuery(final String strSQL) {20 try {21 System.out.println("strSQL>" + strSQL);22 // Cursor相当于JDBC中的ResultSet23 Cursor cursor = db.rawQuery(strSQL, null);24 // 始终让cursor指向数据库表的第1行记录25 cursor.moveToFirst();26 // 定义一个StringBuffer的对象,用于动态拼接字符串27 StringBuffer sb = new StringBuffer();28 // 循环游标,如果不是最后一项记录29 while (!cursor.isAfterLast()) {30 sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"31 + cursor.getString(2) + "/" + cursor.getString(3) + "/"32 + cursor.getString(4)+"#");33 //cursor游标移动34 cursor.moveToNext();35 }36 db.close();37 return sb.deleteCharAt(sb.length()-1).toString();38 } catch (RuntimeException e) {39 e.printStackTrace();40 return null;41 }42 43 }44 45 // 写操作46 public boolean execOther(final String strSQL) {47 db.beginTransaction(); //开始事务48 try {49 System.out.println("strSQL" + strSQL);50 db.execSQL(strSQL);51 db.setTransactionSuccessful(); //设置事务成功完成 52 db.close();53 return true;54 } catch (RuntimeException e) {55 e.printStackTrace();56 return false;57 }finally { 58 db.endTransaction(); //结束事务 59 } 60 61 }62 }复制代码我们在Dao构造方法中实例化sqliteDBHelper并获取一个SQLiteDatabase对象,作为整个应用的数据库实例;在增删改信息时,我们采用了事务处理,确保数据完整性;最后要注意释放数据库资源db.close(),这一个步骤在我们整个应用关闭时执行,这个环节容易被忘记,所以朋友们要注意。 我们获取数据库实例时使用了getWritableDatabase()方法,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库实例呢?在这里我想和大家着重分析一下这一点。 我们来看一下SQLiteOpenHelper中的getReadableDatabase()方法: 复制代码 1 public synchronized SQLiteDatabase getReadableDatabase() { 2 if (mDatabase != null && mDatabase.isOpen()) { 3 // 如果发现mDatabase不为空并且已经打开则直接返回 4 return mDatabase; 5 } 6 7 if (mIsInitializing) { 8 // 如果正在初始化则抛出异常 9 throw new IllegalStateException("getReadableDatabase called recursively"); 10 } 11 12 // 开始实例化数据库mDatabase 13 14 try { 15 // 注意这里是调用了getWritableDatabase()方法 16 return getWritableDatabase(); 17 } catch (SQLiteException e) { 18 if (mName == null) 19 throw e; // Can't open a temp database read-only! 20 Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e); 21 } 22 23 // 如果无法以可读写模式打开数据库 则以只读方式打开 24 25 SQLiteDatabase db = null; 26 try { 27 mIsInitializing = true; 28 String path = mContext.getDatabasePath(mName).getPath();// 获取数据库路径 29 // 以只读方式打开数据库 30 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY); 31 if (db.getVersion() != mNewVersion) { 32 throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " 33 + mNewVersion + ": " + path); 34 } 35 36 onOpen(db); 37 Log.w(TAG, "Opened " + mName + " in read-only mode"); 38 mDatabase = db;// 为mDatabase指定新打开的数据库 39 return mDatabase;// 返回打开的数据库 40 } finally { 41 mIsInitializing = false; 42 if (db != null && db != mDatabase) 43 db.close(); 44 } 45 }复制代码在getReadableDatabase()方法中,首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。既然有可能调用到getWritableDatabase()方法,我们就要看一下了: 复制代码public synchronized SQLiteDatabase getWritableDatabase() { if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { // 如果mDatabase不为空已打开并且不是只读模式 则返回该实例 return mDatabase; } if (mIsInitializing) { throw new IllegalStateException("getWritableDatabase called recursively"); } // If we have a read-only database open, someone could be using it // (though they shouldn't), which would cause a lock to be held on // the file, and our attempts to open the database read-write would // fail waiting for the file lock. To prevent that, we acquire the // lock on the read-only database, which shuts out other users. boolean success = false; SQLiteDatabase db = null; // 如果mDatabase不为空则加锁 阻止其他的操作 if (mDatabase != null) mDatabase.lock(); try { mIsInitializing = true; if (mName == null) { db = SQLiteDatabase.create(null); } else { // 打开或创建数据库 db = mContext.openOrCreateDatabase(mName, 0, mFactory); } // 获取数据库版本(如果刚创建的数据库,版本为0) int version = db.getVersion(); // 比较版本(我们代码中的版本mNewVersion为1) if (version != mNewVersion) { db.beginTransaction();// 开始事务 try { if (version == 0) { // 执行我们的onCreate方法 onCreate(db); } else { // 如果我们应用升级了mNewVersion为2,而原版本为1则执行onUpgrade方法 onUpgrade(db, version, mNewVersion); } db.setVersion(mNewVersion);// 设置最新版本 db.setTransactionSuccessful();// 设置事务成功 } finally { db.endTransaction();// 结束事务 } } onOpen(db); success = true; return db;// 返回可读写模式的数据库实例 } finally { mIsInitializing = false; if (success) { // 打开成功 if (mDatabase != null) { // 如果mDatabase有值则先关闭 try { mDatabase.close(); } catch (Exception e) { } mDatabase.unlock();// 解锁 } mDatabase = db;// 赋值给mDatabase } else { // 打开失败的情况:解锁、关闭 if (mDatabase != null) mDatabase.unlock(); if (db != null) db.close(); } } }复制代码大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。 看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在DBManager构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取数据实例,如果遇到异常,再试图用getReadableDatabase()获取实例,当然这个时候你获取的实例只能读不能写了 最后,让我们看一下如何使用这些数据操作方法来显示数据,界面核心逻辑代码: 复制代码public class SQLiteActivity extends Activity { public DiaryDao diaryDao; //因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory); //所以要确保context已初始化,我们可以把实例化Dao的步骤放在Activity的onCreate里 @Override protected void onCreate(Bundle savedInstanceState) { diaryDao = new DiaryDao(SQLiteActivity.this); initDatabase(); } class ViewOcl implements View.OnClickListener { @Override public void onClick(View v) { String strSQL; boolean flag; String message; switch (v.getId()) { case R.id.btnAdd: String title = txtTitle.getText().toString().trim(); String weather = txtWeather.getText().toString().trim();; String context = txtContext.getText().toString().trim();; String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date()); // 动态组件SQL语句 strSQL = "insert into diary values(null,'" + title + "','" + weather + "','" + context + "','" + publish + "')"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"添加成功":"添加失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; case R.id.btnDelete: strSQL = "delete from diary where tid = 1"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"删除成功":"删除失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; case R.id.btnQuery: strSQL = "select * from diary order by publish desc"; String data = diaryDao.execQuery(strSQL); Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show(); break; case R.id.btnUpdate: strSQL = "update diary set title = '测试标题1-1' where tid = 1"; flag = diaryDao.execOther(strSQL); //返回信息 message = flag?"更新成功":"更新失败"; Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); break; } } } private void initDatabase() { // 创建数据库对象 SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this); sqliteDBHelper.getWritableDatabase(); System.out.println("数据库创建成功"); } }复制代码 Android sqlite3数据库管理工具 Android SDK的tools目录下提供了一个sqlite3.exe工具,这是一个简单的sqlite数据库管理工具。开发者可以方便的使用其对sqlite数据库进行命令行的操作。 程序运行生成的.db文件一般位于"/data/data/项目名(包括所处包名)/databases/.db",因此要对数据库文件进行操作需要先找到数据库文件: 1、进入shell 命令 adb shell2、找到数据库文件 cd data/data ls --列出所有项目 cd project_name --进入所需项目名 cd databases ls --列出现寸的数据库文件 3、进入数据库 sqlite3 test_db --进入所需数据库 会出现类似如下字样: SQLite version 3.6.22Enter ".help" for instructionsEnter SQL statements terminated with a ";"sqlite>至此,可对数据库进行sql操作。 4、sqlite常用命令 .databases --产看当前数据库.tables --查看当前数据库中的表.help --sqlite3帮助.schema --各个表的生成语句 原文地址https://www.cnblogs.com/ITtangtang/p/3920916.html
auto_answer 2019-12-02 01:50:21 0 浏览量 回答数 0

问题

对症下药:Tomcat停机过程分析与线程处理方法

工作中经常遇到因为Tomcat shutdown时自身创建的线程没有及时停止而引起的各种莫名其妙的报错,这篇文章将通过对Tomcat停机过程的梳理,讨论产生这些错误的原因,同时提出了两个可行的解决办...
驻云科技 2019-12-01 21:36:46 4001 浏览量 回答数 0

问题

struts2+mybatis+spring3报错?高手指导!?报错

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'blogDaoImpl': Injection ...
爱吃鱼的程序员 2020-06-22 10:48:26 0 浏览量 回答数 1

问题

从HTML中攫取你所需的信息:报错

本文并非想通过分析HTML的语法然后从中解析出数据,这样做实现困难而且没有什么实际应用的意义,或者应该这样说:我们并不想自己去实现一个HTML语 法的分析器。我们要做的仅仅是从HTML中提取我们所需...
kun坤 2020-06-08 11:01:51 3 浏览量 回答数 1

问题

Hibernate的优点,为什么用Hibernate:报错

一、Hibernate是JDBC的轻量级的对象封装,它是一个独立的对象持久层框架,和 App Server,和EJB没有什么必然的联系。Hibernate可以用在任何JDBC可以使用的场合ÿ...
kun坤 2020-06-09 10:46:45 1 浏览量 回答数 1

问题

Mantis 安装配置手册:报错

作者:龚云卿 2006-4-28 1 所需软件 1.1 MySQL MySQL-server-4.1.10-0.i386.rpm MySQL-client-4.1.10...
kun坤 2020-06-06 14:10:01 0 浏览量 回答数 1

问题

【精品问答】前端开发必懂之JS技术二百问

1.document load 和 document ready 的区别 2.JavaScript 中如何检测一个变量是一个 String 类型? 3.请用 js 去除字符串空格? 4.js 是一门怎样的语言&#...
茶什i 2019-12-01 22:05:04 146 浏览量 回答数 0

问题

【精品问答】Java技术1000问(1)

为了方便Java开发者快速找到相关技术问题和答案,开发者社区策划了Java技术1000问内容,包含最基础的如何学Java、实践中遇到的技术问题、RocketMQ面试、Java容器部署实践等维度内容。 我们会以每...
问问小秘 2019-12-01 21:57:43 46087 浏览量 回答数 16

云产品推荐

上海奇点人才服务相关的云产品 小程序定制 上海微企信息技术相关的云产品 国内短信套餐包 ECS云服务器安全配置相关的云产品 开发者问答 阿里云建站 自然场景识别相关的云产品 万网 小程序开发制作 视频内容分析 视频集锦 代理记账服务 阿里云AIoT