
我是一个小小的程序媛!
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。 spring Boot 特点 1. 创建独立的Spring应用程序 2. 嵌入的Tomcat,无需部署WAR文件 3. 简化Maven配置 4. 自动配置Spring 。。。。。。 maven项目创建 1、访问http://start.spring.io/ 2、选择构建工具Maven Project、Spring Boot版本1.3.6以及一些工程基本信息,点击“Switch to the full version.”java版本选择1.7,可参考下图所示: 3、点击Generate Project下载项目压缩包 4、解压后,使用eclipse,Import -> Existing Maven Projects -> Next ->选择解压后的文件夹-> Finsh,OK done! 项目结构介绍 如上图所示,Spring Boot的基础结构共三个文件: src/main/java 程序开发以及主程序入口 src/main/resources 配置文件 src/test/java 测试程序 另外,spingboot建议的目录结果如下:root package结构:com.example.myproject com +- example +- myproject +- Application.java | +- domain | +- Customer.java | +- CustomerRepository.java | +- service | +- CustomerService.java | +- controller | +- CustomerController.java | 1、Application.java 建议放到根目录下面,主要用于做一些框架配置 2、domain目录主要用于实体(Entity)与数据访问层(Repository) 3、service 层主要是业务类代码 4、controller 负责页面访问控制 采用默认配置可以省去很多配置,当然也可以根据自己的喜欢来进行更改最后,启动Application main方法,至此一个java项目搭建好了! 引入web模块 1、pom.xml中添加支持web的模块: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> pom.xml文件中默认有两个模块: spring-boot-starter :核心模块,包括自动配置支持、日志和YAML; spring-boot-starter-test :测试模块,包括JUnit、Hamcrest、Mockito。 2、编写controller内容: @RestController public class HelloWorldController { @RequestMapping("/hello") public String index() { return "Hello World"; } } @RestController 的意思就是controller里面的方法都以json格式输出,不用再写什么jackjson配置的了! 3、启动主程序,打开浏览器访问http://localhost:8080/hello,就可以看到效果了,有木有很简单! 如何做单元测试 打开的src/test/下的测试入口,编写简单的http请求来测试;使用mockmvc进行,利用MockMvcResultHandlers.print()打印出执行结果。 @RunWith(SpringRunner.class) @SpringBootTest public class HelloTests { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build(); } @Test public void getHello() throws Exception { mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo("Hello World"))); } } 开发环境的调试 热启动在正常开发项目中已经很常见了吧,虽然平时开发web项目过程中,改动项目启重启总是报错;但springBoot对调试支持很好,修改之后可以实时生效,需要添加以下的配置: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin> </plugins> </build> 该模块在完整的打包环境下运行的时候会被禁用。如果你使用java -jar启动应用或者用一个特定的classloader启动,它会认为这是一个“生产环境”。 总结 使用spring boot可以非常方便、快速搭建项目,使我们不用关心框架之间的兼容性,适用版本等各种问题,我们想使用任何东西,仅仅添加一个配置就可以,所以使用sping boot非常适合构建微服务。 学习文章来自 :springboot(一)入门篇 一边学习一边记录。看了就忘了,就还是自己敲敲代码,记录学习的过程。
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
今天被自己给蠢死了 今天在代码中遇到这个错误, 百度翻译一下:映射方法,从一org.system.mapper.child.chmorganizationexaminationmapper.delete返回零作为一个原始的方法的返回类型(int)。 org.apache.ibatis.binding.BindingException: Mapper method 'org.system.mapper.child.ChmOrganizationExaminationMapper.delete attempted to return null from a method with a primitive return type (int). at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:83) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53) at com.sun.proxy.$Proxy44.delete(Unknown Source) at org.system.service.impl.child.ChmOrganizationExaminationService.deleteChmOrganizationExamination(ChmOrganizationExaminationService.java:24) at org.system.service.impl.child.ChmOrganizationExaminationService$$FastClassBySpringCGLIB$$a406e58d.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) at org.system.service.impl.child.ChmOrganizationExaminationService$$EnhancerBySpringCGLIB$$7bf0512.deleteChmOrganizationExamination(<generated>) at org.system.controller.impl.child.ChmOrganizationExaminationController.insertChmOrganizationExamination(ChmOrganizationExaminationController.java:35) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.system.intercept.ValidInterceptor.invoke(ValidInterceptor.java:63) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) at com.sun.proxy.$Proxy96.insertChmOrganizationExamination(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) 最后发现的mapper.xml 代码是这样写的:居然用的select。 <select id="delete" parameterType="org.system.entity.child.ChmTransferOrganization"> DELETE FROM chm_organization_examination WHERE organization_id=#{organizationId} </select> 真的是蠢到家了!
触发器通用属性: jobKey:表示job实例的标识,触发器被触发时,指定的job实例会被执行。 startTime:表示触发器的时间表 首次被触发的时间,他的值类型为java.uti.Date。 endTime:指定的触发器不在被执行的时间,他的值类型为java.uti.Date。 实现: package quartz; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class SchedulerClass { public static void main(String[] args) throws SchedulerException { Date startDate = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:"+sf.format(startDate)); // 创建一个JobDetail实例 // 将该实例与JobClass绑定,JobDetail是由Builder模式来创建的,JobDetail是来绑定job的。 // withIdentity 创建唯一的标识 JobDetail jobDetail = JobBuilder.newJob(JobClass.class).withIdentity("jobName", "group").build(); /*获取当前三秒后的时间*/ startDate.setTime(startDate.getTime()+3000); /*获取当前六秒后的时间*/ Date endDate = new Date(); endDate.setTime(endDate.getTime()+6000); // 创建一个Tigger实例,定义 job立即执行,并每隔一段时间重复执行,每隔5秒中执行一次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger").startAt(startDate).endAt(endDate) .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build(); // 创建一个Scheduler实例, Scheduler是由工厂模式来创建的 so SchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); scheduler.start(); // 调度器调用任务和触发器,scheduler将job,trigger绑定在一起。 scheduler.scheduleJob(jobDetail, trigger); } } package quartz; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.Trigger; public class JobClass implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { Date date=new Date(); SimpleDateFormat sf=new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); System.out.println("执行时间:"+sf.format(date)); // 编写业务逻辑 System.out.println("hello Quartz"); JobKey jobKey=arg0.getJobDetail().getKey(); Trigger triiger=arg0.getTrigger(); System.out.println("开始时间"+sf.format(triiger.getStartTime())); System.out.println("结束时间"+sf.format(triiger.getEndTime())); System.out.println("实例名称:"+jobKey.getName()); } } 执行结果为: SimpleTrigger 作用:在指定的时间段内执行一次作业任务 或是 在指定的时间间隔内执行多次作业任务 (定时定频率) // 定时 当前时间3秒后执行 startDate.setTime(startDate.getTime() + 3000); // Trigger trigger =TriggerBuilder.newTrigger().withIdentity("trigger").startAt(startDate).build(); // 定频率 每隔3秒中执行一次,执行2次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(2)).build(); 注意: 重复次数,可以是0、正整数,以及常量SimpleTrigger.REPEAT_INDEFINITELY。重复的间隔,必须是0,或者long型的正数,表示毫秒。注意,如果重复间隔为0,trigger将会以重复次数并发执行(或者以scheduler可以处理的近似并发数)。 endTime属性的值会覆盖设置重复次数的属性值;比如,你可以创建一个trigger,在终止时间之前每隔10秒执行一次,你不需要去计算在开始时间和终止时间之间的重复次数,只需要设置终止时间并将重复次数设置为REPEAT_INDEFINITELY(当然,你也可以将重复次数设置为一个很大的值,并保证该值比trigger在终止时间之前实际触发的次数要大即可)。 CronTrigger:基于日历的作业调度器。 Cron表达式: 用于配置CronTrigger实例。由7个子表达式组成的字符串,描述时间表的详细信息,格式:秒 分 时 日 月 周 年 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger") .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *")).build(); 通配符说明 实在是不知道怎么写 网上搜搜 cron在线生成表达式。
一:定义 Job:实现任务逻辑的接口。 JobDeatil:JobDeatil为Job提供了许多设置属性,以及JobDataMap成员变量属性,他用来储存特定的Job实例状态信息,调度器需要使用JobDeatil对象添加Job实例。 二: Job接口源码: public interface Job { void execute(JobExecutionContext context) throws JobExecutionException; } Job有且只有一个方法:execute() 负责执行业务逻辑。 JobExecutionContext:包含job执行的上下文。里面包含了一个重要的类(JobDataMap :主要是包含我们想要输入的参数)。 这个方法要抛出一个异常 JobExecutionException。 JobDeatil的重要属性: name:任务的名称。 group:任务所在的组(默认值:DEFAULT)。 jobClass:任务的实现类。 jobDataMap:传参的作用。 代码是上一节的代码: 实现代码:JobDetail jobDetail = JobBuilder.newJob(JobClass.class).withIdentity("jobName","group").build(); JobDetail jobDetail = JobBuilder.newJob(JobClass.class).withIdentity("jobName","group").build(); System.out.println(jobDetail.getKey().getName());//jobName System.out.println(jobDetail.getKey().getGroup());//group System.out.println(jobDetail.getJobClass().getName());//quartz.JobClass jobDataMap JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。 实现: JobDetail jobDetail = JobBuilder.newJob(JobClass.class).withIdentity("jobName", "group") .usingJobData("Double", 2.0D) .usingJobData("String", "字符串").build(); System.out.println(jobDetail.getJobDataMap().getString("String"));//字符串 System.out.println(jobDetail.getJobDataMap().getDoubleValue("Double"));//2.0 在job的执行过程中,可以从JobDataMap中取出数据 第二种获取的: 是在JobClass里设置属性,这里的属性名称必须和jobDetail设置的key值相同; package quartz; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; public class JobClass implements Job { private String stringValue; private Double doubleValue; public String getStringValue() { return stringValue; } public void setStringValue(String stringValue) { this.stringValue = stringValue; } public Double getDoubleValue() { return doubleValue; } public void setDoubleValue(Double doubleValue) { this.doubleValue = doubleValue; } public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println(stringValue + doubleValue); /* * JobKey jobKey=arg0.getJobDetail().getKey(); * System.out.println(jobKey.getGroup()+jobKey.getName()); */ /* * Date date=new Date(); SimpleDateFormat sf=new SimpleDateFormat( * "yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间为:"+sf.format(date)); * // 编写业务逻辑 System.out.println("hello Quartz"); */ /* * JobDataMap jobDataMap=arg0.getJobDetail().getJobDataMap(); * System.out.println(jobDataMap.getDouble("Double")); * System.out.println(jobDataMap.getString("String")); */ } } 三:生命周期 每次在调度器在执行job的时候,他是在execute()方法前创建一个新的job实例。当调用完之后,关联的job对象实例会被释放,释放之后将会被垃圾回收机制回收。
1 我们使用maven项目 2 创建一个job类,在execute()方法里写上业务逻辑代码。 3 在另外一个类中创建触发器,调度器,并且绑定job。 首先在项目的pom.xml引入需要的jar包。 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency> 创建一个job package quartz; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class JobClass implements Job { public void execute(JobExecutionContext arg0) throws JobExecutionException { Date date=new Date(); SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间为:"+sf.format(date)); // 编写业务逻辑 System.out.println("hello Quartz"); } } 再创建另外一个类 package quartz; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; public class SchedulerClass { public static void main(String[] args) throws SchedulerException { // 创建一个JobDetail实例 将该实例与JobClass绑定,JobDetail是由Builder模式来创建的,JobDetail是来绑定job的。 // withIdentity 创建唯一的标识 JobDetail jobDetail = JobBuilder.newJob(JobClass.class).withIdentity("job").build(); // 创建一个Tigger实例,定义 job立即执行,并每隔一段时间重复执行,每隔5秒中执行一次 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build(); // 创建一个Scheduler实例, Scheduler是由工厂模式来创建的 so SchedulerFactory factory = new StdSchedulerFactory(); Scheduler scheduler = factory.getScheduler(); scheduler.start(); Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间为:" + sf.format(date)); //调度器调用任务和触发器,scheduler将job,trigger绑定在一起。 scheduler.scheduleJob(jobDetail, trigger); } } 但是这样看起来很复杂的!
首先大概的了解一下Quartz。 一:首先进入官网去看看什么是quartz。http://www.quartz-scheduler.org/ Quartz是一个功能丰富的开源作业调度库,可以集成到几乎任何Java应用程序中 - 从最小的独立应用程序到最大的电子商务系统。石英可用于创建执行数十,数百甚至数十万个作业的简单或复杂的计划; 任务定义为标准Java组件的任务,可以执行任何可以对其进行编程的任何内容。Quartz Scheduler包含许多企业级功能,例如支持JTA事务和集群。Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。 二:Quartz 具有以下特点: 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求; 灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式; 分布式和集群能力。 另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。 三:Quartz主要用到的设计模式 Builder模式 Factory模式 组件模式 链式写法 四:核心概念 调度器(Scheduler):定时定频率的去执行任务 任务(Job):被调度的任务(业务逻辑) 触发器(Trigger):生效的时间 五:主要组成的部分 Job JobDetail JobBuilder JobStore Trigger TriggerBuilder ThreadPool Scheduler Calendar:一个Trigger可以和多个Calendar关联,以排除或包含某些时间点 监听器:JobListener TriggerListener SchedulerListener。
转自:(此处更详细)http://blog.csdn.net/gulu_gulu_jp/article/details/50994003 二、项目部署 我们对于 Java Web 项目在本地机器(无论是 Windows 还是 Linux)上的部署已经了然于心了,那么对于在云服务器上部署 Java Web 项目又是如何操作的呢? 其实很简单,还是离不开 Web 项目部署的那三点:① 基础的 JDK 环境② 一个 Web 服务器。如 Tomcat、JBoss③ 一款数据库。如:mysql 对于云服务器上 Java Web 项目的部署,和平时在 Windows、Linux 下部署是一样的。最多也就是只能使用纯命令模式来操作而已,其实过程都一样。 2.1 环境准备 既然是 Java Web 项目的部署,首先自然需要配置好相关的环境罗。也就是:先在云主机上搭建好 Java 开发环境,如 OpenJDK 的安装、Tomcat 的安装、Mysql 数据库的安装。 2.2 项目部署 我们要部署 Java Web 项目,那就至少得将项目上传到云服务器。其实过程很简单,基本上也就是下面三个步骤:① 打包上传:将项目打包成 war 文件,然后利用传到远程服务器(在Eclipse中直接将项目导出为.war文件)。② 将 war 文件移动到 Tomcat 目录下的 webapps 下。③ 重启 Tomcat,访问我们的项目。 在这个过程中,我们需要注意。因为一般而已,作为一个 Web 项目,我们肯定是有数据库的使用的。那么数据库部分怎么办呢?其实,只需要将我们已有的数据库转储为 sql 文件,然后将 sql 文件上传到云服务器上执行即可。以 mysql 为例,如下操作: ① 转储为 sql 脚本,并上传:先在本地将我们项目使用的数据库转为 sql 文件,上传到云服务器上 (可以利用 Navicat 将数据库转储为.sql文件)。② 执行 sql:然后进入 mysql 中执行该 sql 文件。(若服务器装有Navicat,可直接用Navicat执行.sql文件,执行前需要选中存放表的数据库,应该与代码中数据库连接语句包含的数据库名保持一致)。 (可能还需要改改配置文件)
我在这个div中添加了多个input。 拼接一下呢。最老的方法。 jquery获取值: var strSel=""; $("[name='jbbm']:checked").each(function(index, element) { strSel += $(this).val() + ","; }); alert("选中的值:"+strSel);
我的谷歌的版本是57的。结果发现浏览器没有设置编码的。今天终于把问题的解决了! 因为谷歌在55版本以后删除了手动设置网站编码的功能。我们需要去安装一个charset插件。在Chrome网上应用商店,收索Charset就会出现。 这个就是我们需要的。 安装完成之后,浏览器的右上角会出现一个标识; 这里就可以设置编码了!
1 NOW() 返回当前的日期和时间。 2 CURDATE() 返回当前的日期。 3 CURTIME() 返回当前的时间。 select NOW() '当前的日期和时间',CURDATE() '当前的日期',CURTIME() '当前的时间' 4 DATE() 函数提取日期或日期/时间表达式的日期部分。 select DATE('2017-06-23 11:39:39') '日期'; 5 EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等等。EXTRACT(unit FROM date) date 参数是合法的日期表达式。unit 参数可以是下列的值同DATE_SUB() SELECT EXTRACT(YEAR FROM '2017-06-23 11:39:39') AS OrderYear, EXTRACT(MONTH FROM '2017-06-23 11:39:39') AS OrderMonth, EXTRACT(DAY FROM '2017-06-23 11:39:39') AS OrderDay 6 DATE_ADD() 函数向日期添加指定的时间间隔。DATE_SUB(date,INTERVAL expr type) date 参数是合法的日期表达式。expr 参数是您希望添加的时间间隔。type值同DATE_SUB() SELECT DATE_ADD('2017-06-23 11:39:39',INTERVAL 45 DAY) AS OrderPayDay,DATE_ADD('2017-06-23 11:39:39',INTERVAL 2 YEAR) AS OrderPayYear 7 DATE_SUB() 函数从日期减去指定的时间间隔。 DATE_SUB(date,INTERVAL expr type) date 参数是合法的日期表达式。expr 参数是您希望添加的时间间隔。 SELECT DATE_SUB(now(),interval 1 month) subtractDate type 参数可以是下列值: Type 值 MICROSECOND SECOND MINUTE HOUR DAY WEEK MONTH QUARTER YEAR SECOND_MICROSECOND MINUTE_MICROSECOND MINUTE_SECOND HOUR_MICROSECOND HOUR_SECOND HOUR_MINUTE DAY_MICROSECOND DAY_SECOND DAY_MINUTE DAY_HOUR YEAR_MONTH 8 DATEDIFF() 函数返回两个日期之间的天数。 DATEDIFF(date1,date2) date1 和 date2 参数是合法的日期或日期/时间表达式。 注释:只有值的日期部分参与计算。 SELECT DATEDIFF('2017-06-23 13:35:49','2017-05-23 13:35:49') AS diffDate 9 DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据。DATE_FORMAT(date,format) date 参数是合法的日期。format 规定日期/时间的输出格式。 select DATE_FORMAT(NOW(),'%Y - %m - %d') 格式 描述 %a 缩写星期名 %b 缩写月名 %c 月,数值 %D 带有英文前缀的月中的天 %d 月的天,数值(00-31) %e 月的天,数值(0-31) %f 微秒 %H 小时(00-23) %h 小时(01-12) %I 小时(01-12) %i 分钟,数值(00-59) %j 年的天(001-366) %k 小时(0-23) %l 小时(1-12) %M 月名 %m 月,数值(00-12) %p AM 或 PM %r 时间,12-小时(hh:mm:ss AM 或 PM) %S 秒(00-59) %s 秒(00-59) %T 时间, 24-小时(hh:mm:ss) %U 周(00-53)星期日是一周的第一天 %u 周(00-53)星期一是一周的第一天 %V 周(01-53)星期日是一周的第一天,与 %X 使用 %v 周(01-53)星期一是一周的第一天,与 %x 使用 %W 星期名 %w 周的天(0=星期日, 6=星期六) %X 年,其中的星期日是周的第一天,4 位,与 %V 使用 %x 年,其中的星期一是周的第一天,4 位,与 %v 使用 %Y 年,4 位 %y 年,2 位
html; 1 <input id="cc" class="easyui-combobox" value="" data-options=" 2 url:'xxxxxx', 3 method:'get', 4 valueField:'id', 5 textField:'groupName', 6 multiple:true, 7 panelHeight:'auto', 8 loadFilter:function(data){ 9 return data.data.rows; 10 }" 11 /> 12 <a id="btnGet" class="easyui-linkbutton" data-options="iconCls:'icon-add'">获取已选项集合值</a> js: 1 $("#btnGet").click(function () { 2 alert( $('#cc').combobox('getValues')); 3 }); 效果: 设置value值;value="12,13" 打开的时候会自动的选中。
如果在你的项目中,需要使用到的jar包。 如果使用build path的话,你把你的项目给别人运行的时候,别人还需要去build path,如果要换jar包的版本,这些都是比较麻烦的,所以我们使用maven从存储库下载。 org.jvnet.localizer 只适用于 Java.net资源库 <dependency> <groupId>org.jvnet.localizer</groupId> <artifactId>localizer</artifactId> <version>1.8</version> </dependency> 当你建立这个 Maven 项目,它将依赖找不到失败并输出错误消息。 2. 声明Java.net储存库 告诉 Maven 来获得 Java.net 的依赖,你需要声明远程仓库在 pom.xml 文件这样: pom.xml <repositories> <repository> <id>java.net</id> <url>https://maven.java.net/content/repositories/public/</url> </repository> </repositories> 现在,Maven的依赖库查询顺序更改为: 在 Maven 本地资源库中搜索,如果没有找到,进入第 2 步,否则退出。 在 Maven 中央存储库搜索,如果没有找到,进入第 3 步,否则退出。 在java.net Maven的远程存储库搜索,如果没有找到,提示错误信息,否则退出。 文章来自:http://www.yiibai.com/maven/
CONF window7 本地资源库: 一般默认的地址:C:\Users\Administrator\.m2 也可以修改地址:在路径{M2_HOME}\conf\setting.xml,更新 localRepository 到其它名称 eg: 中央存储库 :http://search.maven.org/#search%7Cga%7C1%7Cpoi 当你建立一个 Maven 的项目,Maven 会检查你的 pom.xml 文件,以确定哪些依赖下载。首先,Maven 将从本地资源库获得 Maven 的本地资源库依赖资源,如果没有找到,然后把它会从默认的 Maven 中央存储库去下载。 文章来自:http://www.yiibai.com/maven/
所需工具 : JDK 1.8 Maven 3.3.9 Windows 7 下载Maven 3.3.9 http://maven.apache.org/download.cgi 首先要先安装JDK. 配置环境: 添加到path后面: 运行: 出现这些说明安装成功了!
先看看图片 -要求 刚开始呢 我只是简单的认为查询开始和结束时间之间的就可以了。 select * from table fp where WHERE fp.preferential_start_time >= ' 2012-12-11 10:31:36.0' and fp.preferential_end_time <= '2012-12-28 10:31:36.0' 但是这样是查询不出来数据的。 结果sql语句是需要这样来写: select * from table fp where ( fp.preferential_start_time >= '2012-12-11 10:31:36.0' and fp.preferential_end_time <='2012-12-28 10:31:36.0' )or ( fp.preferential_start_time >= '2012-12-11 10:31:36.0' and fp.preferential_start_time < '2012-12-28 10:31:36.0') or ( fp.preferential_end_time > '2012-12-11 10:31:36.0' and fp.preferential_end_time <='2012-12-28 10:31:36.0') 好好体会一下呢!
转载来自:http://blog.sina.com.cn/s/blog_7c5d61f30101cx0q.html 举例1: 使用该查询,得出iFavoriteID,iFavUserType ,cUser,iArticleID,dFavoriteTime五个字段的值:SELECT iFavoriteID,CASE WHEN iFavUserType = 0 THEN '新闻管理员'WHEN iFavUserType = 1 THEN '商家'WHEN iFavUserType = 2 THEN '会员'WHEN iFavUserType = 3 THEN '未注册'WHEN iFavUserType = 4 then '匿名'END AS iFavUserType, cUser, iArticleID,CONVERT(nvarchar(100), dFavoriteTime, 111) AS dFavoriteTime FROM dig_favorite 举例2: SELECT CASE WHEN `MEMBERTYPE` =1THEN '参赛队员'ELSE '指导老师'END FROM `tab_sign_member`WHERE 1
1 package com.cy; 59 import java.security.InvalidParameterException; 60 import java.text.ParseException; 61 import java.text.SimpleDateFormat; 62 import java.util.ArrayList; 63 import java.util.Calendar; 64 import java.util.Date; 65 import java.util.HashMap; 66 import java.util.Map; 67 68 public class DateUtils { 69 private static final long MILLIS_IN_A_SECOND = 1000; 70 71 private static final long SECONDS_IN_A_MINUTE = 60; 72 73 private static final int MONTHS_IN_A_YEAR = 12; 74 75 /** 76 * 获得指定日期之后一段时期的日期。例如某日期之后3天的日期等 时间为23:59:59。 77 * 78 * @param origDate 79 * 基准日期 80 * @param amount 81 * 时间数量 82 * @param timeUnit 83 * 时间单位,如年、月、日等。用Calendar中的常量代表 84 * @return {@link Date} 85 */ 86 public static final Date dateAfter(Date origDate, int amount, int timeUnit) { 87 Calendar calendar = Calendar.getInstance(); 88 calendar.setTime(origDate); 89 // 将小时至0 90 // calendar.set(Calendar.HOUR_OF_DAY, 23); 91 // 将分钟至0 92 // calendar.set(Calendar.MINUTE, 59); 93 // 将秒至0 94 // calendar.set(Calendar.SECOND, 59); 95 // 将毫秒至0 96 // calendar.set(Calendar.MILLISECOND, 0); 97 calendar.add(timeUnit, amount); 98 return calendar.getTime(); 99 } 100 101 /** 102 * 获得指定日期之后一段时期的日期。例如某日期之后3天的日期等。 103 * 104 * @param origDate 105 * 基准日期 106 * @param amount 107 * 时间数量 108 * @param timeUnit 109 * 时间单位,如年、月、日等。用Calendar中的常量代表 110 * @return {@link Date} 111 */ 112 public static final Date timeAfter(Date origDate, int amount, int timeUnit) { 113 Calendar calendar = Calendar.getInstance(); 114 calendar.setTime(origDate); 115 calendar.add(timeUnit, amount); 116 return calendar.getTime(); 117 } 118 119 /** 120 * 获得指定日期之前一段时期的日期。例如某日期之前3天的日期等。 121 * 122 * @param origDate 123 * 基准日期 124 * @param amount 125 * 时间数量 126 * @param timeUnit 127 * 时间单位,如年、月、日等。用Calendar中的常量代表 128 * @return {@link Date} 129 */ 130 public static final Date dateBefore(Date origDate, int amount, int timeUnit) { 131 Calendar calendar = Calendar.getInstance(); 132 calendar.add(timeUnit, -amount); 133 return calendar.getTime(); 134 } 135 136 /** 137 * 根据年月日构建日期对象。注意月份是从1开始计数的,即month为1代表1月份。 138 * 139 * @param year 140 * 年 141 * @param month 142 * 月。注意1代表1月份,依此类推。 143 * @param day 144 * 日 145 * @return {@link Date} 146 */ 147 public static Date date(int year, int month, int date) { 148 Calendar calendar = Calendar.getInstance(); 149 calendar.set(year, month - 1, date, 0, 0, 0); 150 calendar.set(Calendar.MILLISECOND, 0); 151 return calendar.getTime(); 152 } 153 154 /** 155 * 计算两个日期(不包括时间)之间相差的周年数 156 * 157 * @param date1 158 * 开始时间 159 * @param date2 160 * 结束时间 161 * @return {@link Integer} 162 */ 163 public static int getYearDiff(Date date1, Date date2) { 164 if (date1 == null || date2 == null) { 165 throw new InvalidParameterException("date1 and date2 cannot be null!"); 166 } 167 if (date1.after(date2)) { 168 throw new InvalidParameterException("date1 cannot be after date2!"); 169 } 170 171 Calendar calendar = Calendar.getInstance(); 172 calendar.setTime(date1); 173 int year1 = calendar.get(Calendar.YEAR); 174 int month1 = calendar.get(Calendar.MONTH); 175 int day1 = calendar.get(Calendar.DATE); 176 177 calendar.setTime(date2); 178 int year2 = calendar.get(Calendar.YEAR); 179 int month2 = calendar.get(Calendar.MONTH); 180 int day2 = calendar.get(Calendar.DATE); 181 182 int result = year2 - year1; 183 if (month2 < month1) { 184 result--; 185 } else if (month2 == month1 && day2 < day1) { 186 result--; 187 } 188 return result; 189 } 190 191 /** 192 * 计算两个日期(不包括时间)之间相差的整月数 193 * 194 * @param date1 195 * 开始时间 196 * @param date2 197 * 结束时间 198 * @return {@link Integer} 199 */ 200 public static int getMonthDiff(Date date1, Date date2) { 201 if (date1 == null || date2 == null) { 202 throw new InvalidParameterException("date1 and date2 cannot be null!"); 203 } 204 if (date1.after(date2)) { 205 throw new InvalidParameterException("date1 cannot be after date2!"); 206 } 207 208 Calendar calendar = Calendar.getInstance(); 209 calendar.setTime(date1); 210 int year1 = calendar.get(Calendar.YEAR); 211 int month1 = calendar.get(Calendar.MONTH); 212 int day1 = calendar.get(Calendar.DATE); 213 214 calendar.setTime(date2); 215 int year2 = calendar.get(Calendar.YEAR); 216 int month2 = calendar.get(Calendar.MONTH); 217 int day2 = calendar.get(Calendar.DATE); 218 219 int months = 0; 220 if (day2 >= day1) { 221 months = month2 - month1; 222 } else { 223 months = month2 - month1 - 1; 224 } 225 return (year2 - year1) * MONTHS_IN_A_YEAR + months; 226 } 227 228 /** 229 * 统计两个日期之间包含的天数。包含date1,但不包含date2 230 * 231 * @param date1 232 * 开始时间 233 * @param date2 234 * 结束时间 235 * @return {@link Integer} 236 * @throws ParseException 237 */ 238 public static int getDayDiff(Date date1, Date date2) { 239 if (date1 == null || date2 == null) { 240 throw new InvalidParameterException("date1 and date2 cannot be null!"); 241 } 242 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 243 Date smdate; 244 try { 245 smdate = sdf.parse(sdf.format(date1)); 246 Date bdate = sdf.parse(sdf.format(date2)); 247 Calendar cal = Calendar.getInstance(); 248 cal.setTime(smdate); 249 long time1 = cal.getTimeInMillis(); 250 cal.setTime(bdate); 251 long time2 = cal.getTimeInMillis(); 252 long between_days = (time2 - time1) / (1000 * 3600 * 24); 253 return Integer.parseInt(String.valueOf(between_days)); 254 } catch (ParseException e) { 255 e.printStackTrace(); 256 } 257 return 0; 258 } 259 260 /** 261 * 计算time2比time1晚多少分钟,忽略日期部分 262 * 263 * @param time1 264 * @param time2 265 * @return 266 */ 267 public static int getMinuteDiffByTime(Date time1, Date time2) { 268 long startMil = 0; 269 long endMil = 0; 270 Calendar calendar = Calendar.getInstance(); 271 calendar.setTime(time1); 272 calendar.set(1900, 1, 1); 273 startMil = calendar.getTimeInMillis(); 274 calendar.setTime(time2); 275 calendar.set(1900, 1, 1); 276 endMil = calendar.getTimeInMillis(); 277 return (int) ((endMil - startMil) / MILLIS_IN_A_SECOND / SECONDS_IN_A_MINUTE); 278 } 279 280 /** 281 * 计算时间是否是同一天 282 * 283 * @param dateA 284 * @param dateB 285 * @return 286 */ 287 public static boolean areSameDay(Date dateA, Date dateB) { 288 Calendar calDateA = Calendar.getInstance(); 289 calDateA.setTime(dateA); 290 291 Calendar calDateB = Calendar.getInstance(); 292 calDateB.setTime(dateB); 293 294 return calDateA.get(Calendar.YEAR) == calDateB.get(Calendar.YEAR) 295 && calDateA.get(Calendar.MONTH) == calDateB.get(Calendar.MONTH) 296 && calDateA.get(Calendar.DAY_OF_MONTH) == calDateB.get(Calendar.DAY_OF_MONTH); 297 } 298 299 /** 300 * @Title: getCurrYearLast 301 * @Description: 获取某年最后一天 302 * @param date 303 * @return Date 304 */ 305 public static Date getCurrYearLast(Date date) { 306 Calendar currCal = Calendar.getInstance(); 307 currCal.setTime(date); 308 int currentYear = currCal.get(Calendar.YEAR); 309 return getYearLast(currentYear); 310 } 311 312 private static Date getYearLast(int year) { 313 Calendar calendar = Calendar.getInstance(); 314 calendar.clear(); 315 calendar.set(Calendar.YEAR, year); 316 317 calendar.roll(Calendar.DAY_OF_YEAR, -1); 318 // 将小时至0 319 calendar.set(Calendar.HOUR_OF_DAY, 23); 320 // 将分钟至0 321 calendar.set(Calendar.MINUTE, 59); 322 // 将秒至0 323 calendar.set(Calendar.SECOND, 59); 324 // 将毫秒至0 325 calendar.set(Calendar.MILLISECOND, 0); 326 327 Date currYearLast = calendar.getTime(); 328 return currYearLast; 329 } 330 /* 331 * private static Date getYearLast(int year) { Calendar calendar = 332 * Calendar.getInstance(); calendar.clear(); calendar.set(Calendar.YEAR, 333 * year); calendar.roll(Calendar.DAY_OF_YEAR, -1); Date currYearLast = 334 * calendar.getTime(); return currYearLast; } 335 */ 336 /** 337 * 338 * @param args 339 * @throws ParseException 340 * 341 * 根据结算周期数 对开始时间与结束时间进行分段 342 */ 343 public static void main(String[] args) throws ParseException { 344 /*开始时间*/ 345 Date startDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2017-1-20 10:31:36"); 346 /*结束时间*/ 347 Date endDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2017-5-12 10:31:36"); 348 /*周期数*/ 349 int solt = 2; 350 ArrayList<Map<String, Object>> list = new ArrayList<>(); 351 352 while (startDate.getTime()<endDate.getTime()) { 353 Map<String, Object> map = new HashMap<>(); 354 map.put("开始时间",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startDate)); 355 startDate=dateAfter(startDate, solt, Calendar.MONTH); 356 if (startDate.getTime()>endDate.getTime()) { 357 map.put("结束时间",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(endDate)); 358 359 } else { 360 map.put("结束时间",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startDate)); 361 } 362 list.add(map); 363 } 364 System.out.println(list); 365 } 366 }
/* 开始时间 */ Date leftStartDate = feesPreferential.getPreferentialStartTime(); /* 结束时间 */ Date leftEndDate = feesPreferential.getPreferentialEndTime(); /*比较的时间段*/ Date rightStartDate = (Date) feesPreferentialList.get(i).get("preferentialStartTime"); Date rightEndDate = (Date) feesPreferentialList.get(i).get("preferentialEndTime"); /*判断*/ if (((leftStartDate.getTime() >= rightStartDate.getTime()) && leftStartDate.getTime() < rightEndDate.getTime()) || ((leftStartDate.getTime() > rightStartDate.getTime()) && leftStartDate.getTime() <= rightEndDate.getTime()) || ((rightStartDate.getTime() >= leftStartDate.getTime()) && rightStartDate.getTime() < leftEndDate.getTime()) || ((rightStartDate.getTime() > leftStartDate.getTime()) && rightStartDate.getTime() <= leftEndDate.getTime())){ System.out.println("两段时间存在交集"); } System.out.println("两段时间不存在交集");
给编辑器赋值的代码: var ue = UE.getEditor('content'); ue.ready(function (){ ue.setContent(data.data.content); }); 当我第二次去打开编辑器的时候,编辑器里就有数据了。 大神们帮我看看嘛! 这是怎么回事呢?????????? 找了一天终于解决了,太感谢这位朋友了。 原文地址: https://my.oschina.net/u/2247058/blog/371420 结果发现我第二次打开编辑器的时候,数据还是原来的数据。数据都没变,调试发现,当我第二次打开编辑器的时候,根本就没有执行这条语句。 ue.addListener("ready", function () { // editor准备好之后才可以使用 ue.setContent(data.data.content); }); 继续查找原因 结合两次的代码: 一: ue.addListener("ready",function(){ ue.setContent(data.data.content); return ; }); ue.setContent(data.data.content); 这样有点取巧的感觉: 当我第一次打开编辑器的时候,会执行 ue.addListener("ready",function(){ ue.setContent(data.data.content); return ; }); 这时编辑器里有数据了,当然ue.setContent(data.data.content);这句也会执行,但是编辑器不会显示数据的。 当我第二次打开数据的时候,当然 ue.addListener("ready",function(){ ue.setContent(data.data.content); return ; });这段代码是不会执行的,现在就执行ue.setContent(data.data.content)这段代码,编辑器啊里就有数据了,而且数据也是不同的。 二: 其实使用了监听事件,我们也可以移除事件监听的。我使用了一下,但是我的不行,可能是代码哪里的问题吧! 我觉得这个是可以解决我这个问题的。 暂时就这样了!
join (inner join ) 注释:INNER JOIN 关键字在表中存在至少一个匹配时返回行。 left join 注释:LEFT JOIN 关键字从左表(table1)返回所有的行,即使右表(table2)中没有匹配。如果右表中没有匹配,则结果为 NULL。 right join 注释:RIGHT JOIN 关键字从右表(table2)返回所有的行,即使左表(table1)中没有匹配。如果左表中没有匹配,则结果为 NULL。
在datagrid中添加一句,DataGrid属性中的nowrap:false。 (默认为true)。 注意nowrap不是列属性, <th data-options="field:'roleName',width:60,align:'center',nowrap:false">xxx</th> 这样是不行的。 需要写在datagrid中 eg: html: <table id="_main_table"> <thead> <tr> <th data-options="field:'id',width:20">id</th> <th data-options="field:'doctorName',width:40,align:'center'">医生</th> <th data-options="field:'roleName',width:100,align:'center'">医生角色</th> <th data-options="field:'img',align:'center',width:50,formatter:_opformatter2">操作</th> </tr> </thead> </table> js: $('#_main_table').datagrid({ method:'get', url: 'xxxxx', checkbox:true, pagination: true, fit: true, cache:false, fitColumns: true, border: false, rownumbers: true, nowrap:false, frozenColumns:[[ {field:'id',checkbox:true,hidden:true} ]], loadFilter: function(data) { return data.data; }, }); 效果:
随笔转载自:http://www.cnblogs.com/appleat/archive/2012/09/03/2669033.html 一、CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串。使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为+----+--------+| id | name |+----+--------+| 1 | BioCyc |+----+--------+1、语法及使用特点:CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。可以有一个或多个参数。2、使用示例:SELECT CONCAT(id, ‘,’, name) AS con FROM info LIMIT 1;返回结果为+----------+| con |+----------+| 1,BioCyc |+----------+SELECT CONCAT(‘My’, NULL, ‘QL’);返回结果为+--------------------------+| CONCAT('My', NULL, 'QL') |+--------------------------+| NULL |+--------------------------+3、如何指定参数之间的分隔符使用函数CONCAT_WS()。使用语法为:CONCAT_WS(separator,str1,str2,…)CONCAT_WS() 代表 CONCAT With Separator ,是CONCAT()的特殊形式。第一个参数是其它参数的分隔符。分隔符的位置放在要连接的两个字符串之间。分隔符可以是一个字符串,也可以是其它参数。如果分隔符为 NULL,则结果为 NULL。函数会忽略任何分隔符参数后的 NULL 值。但是CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。如SELECT CONCAT_WS('_',id,name) AS con_ws FROM info LIMIT 1;返回结果为+----------+| con_ws |+----------+| 1_BioCyc |+----------+SELECT CONCAT_WS(',','First name',NULL,'Last Name');返回结果为+----------------------------------------------+| CONCAT_WS(',','First name',NULL,'Last Name') |+----------------------------------------------+| First name,Last Name |+----------------------------------------------+二、GROUP_CONCAT()函数GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成。使用表info作为示例,其中语句SELECT locus,id,journal FROM info WHERE locus IN('AB086827','AF040764');的返回结果为+----------+----+--------------------------+| locus | id | journal |+----------+----+--------------------------+| AB086827 | 1 | Unpublished || AB086827 | 2 | Submitted (20-JUN-2002) || AF040764 | 23 | Unpublished || AF040764 | 24 | Submitted (31-DEC-1997) |+----------+----+--------------------------+1、使用语法及特点:GROUP_CONCAT([DISTINCT] expr [,expr ...][ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] [,col ...]][SEPARATOR str_val])在 MySQL 中,你可以得到表达式结合体的连结值。通过使用 DISTINCT 可以排除重复值。如果希望对结果中的值进行排序,可以使用 ORDER BY 子句。SEPARATOR 是一个字符串值,它被用于插入到结果值中。缺省为一个逗号 (","),可以通过指定 SEPARATOR "" 完全地移除这个分隔符。可以通过变量 group_concat_max_len 设置一个最大的长度。在运行时执行的句法如下: SET [SESSION | GLOBAL] group_concat_max_len = unsigned_integer;如果最大长度被设置,结果值被剪切到这个最大长度。如果分组的字符过长,可以对系统参数进行设置:SET @@global.group_concat_max_len=40000;2、使用示例:语句 SELECT locus,GROUP_CONCAT(id) FROM info WHERE locus IN('AB086827','AF040764') GROUP BY locus; 的返回结果为+----------+------------------+| locus | GROUP_CONCAT(id) |+----------+------------------+| AB086827 | 1,2 || AF040764 | 23,24 |+----------+------------------+语句 SELECT locus,GROUP_CONCAT(distinct id ORDER BY id DESC SEPARATOR '_') FROM info WHERE locus IN('AB086827','AF040764') GROUP BY locus;的返回结果为+----------+----------------------------------------------------------+| locus | GROUP_CONCAT(distinct id ORDER BY id DESC SEPARATOR '_') |+----------+----------------------------------------------------------+| AB086827 | 2_1 || AF040764 | 24_23 |+----------+----------------------------------------------------------+语句SELECT locus,GROUP_CONCAT(concat_ws(', ',id,journal) ORDER BY id DESC SEPARATOR '. ') FROM info WHERE locus IN('AB086827','AF040764') GROUP BY locus;的返回结果为+----------+--------------------------------------------------------------------------+| locus | GROUP_CONCAT(concat_ws(', ',id,journal) ORDER BY id DESC SEPARATOR '. ') |+----------+--------------------------------------------------------------------------+| AB086827 | 2, Submitted (20-JUN-2002). 1, Unpublished || AF040764 | 24, Submitted (31-DEC-1997) . 23, Unpublished |+----------+--------------------------------------------------------------------------+
6.1 自动化测试模型介绍 6.1.1 线性测试 :其实就是单纯地来模拟用户完整的操作场景。 优势就是每一个脚本都是完整且独立的; 缺陷测试用例的开发与维护成本很高。 6.1.2 模块化驱动测试 :编程语言中模块化的思想,把重复的操作独立成公共模块,当用例执行过程中需要用到这一模块操作时则被调用,这样就最大程度上消除了重复,从而提高测试用例的可维护性。 6.1.3 数据驱动测试 :数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变.(我们读取的是定义的数组、字典,或者是外部文件(excel、csv、txt、xml等)都可以看作是数据驱动。) 6.1.4 关键字驱动测试: 6.2 模块化实例 线性测试实例: package com.cy; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class TestModel { public static void main(String[] args) throws InterruptedException { System.out.println("博客园登录 操作 退出"); WebDriver driver =new FirefoxDriver(); driver.get("https://passport.cnblogs.com/user/signin?AspxAutoDetectCookieSupport=1"); // 清除input 输入用户名 driver.findElement(By.id("input1")).clear(); driver.findElement(By.id("input1")).sendKeys("Smile燕"); // 清除input 输入密码 driver.findElement(By.id("input2")).clear(); driver.findElement(By.id("input2")).sendKeys("acy123"); // 点击登录 driver.findElement(By.id("signin")).click(); Thread.sleep(5000); /** * 操作 */ // 退出 driver.findElement(By.linkText("退出")).click(); Thread.sleep(5000); // // 接受弹框 driver.switchTo().alert().accept(); Thread.sleep(5000); // 关闭浏览器 driver.quit(); } } 模块化驱动测试实例: 把登录和退出进行封装。 package com.cy; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class TestModel { public static void main(String[] args) throws InterruptedException { System.out.println("博客园登录 操作 退出"); WebDriver driver =new FirefoxDriver(); /** * 操作 */ login(driver); Thread.sleep(5000); logout(driver); Thread.sleep(5000); // 关闭浏览器 driver.quit(); } public static void login(WebDriver driver) { driver.get("https://passport.cnblogs.com/user/signin?AspxAutoDetectCookieSupport=1"); // 清除input 输入用户名 driver.findElement(By.id("input1")).clear(); driver.findElement(By.id("input1")).sendKeys("Smile燕"); // 清除input 输入密码 driver.findElement(By.id("input2")).clear(); driver.findElement(By.id("input2")).sendKeys("acy123"); // 点击登录 driver.findElement(By.id("signin")).click(); } public static void logout(WebDriver driver){ // 退出 driver.findElement(By.linkText("退出")).click(); // // 接受弹框 driver.switchTo().alert().accept(); } } 我们可以把这两个方法 封装成单独的文件中供其它用例调用。这样对于每个用例来说就简便了许多,也更易于维护。 数据驱动实例:前面提到关于数据驱动的形式有很多,我们既可以通过定义变量的方式进行参数化,也可以通过定义数组、字典的方式进行参数化,还可以通过读取文件(txt\csv\xml)的方式进行参数化。 package com.cy; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class TestModel { public static void main(String[] args) throws InterruptedException { System.out.println("博客园登录 操作 退出"); WebDriver driver =new FirefoxDriver(); /** * 操作 */ String username="Smile燕"; String password="acy123@@"; login(driver,username,password); Thread.sleep(5000); logout(driver); Thread.sleep(5000); // 关闭浏览器 driver.quit(); } public static void login(WebDriver driver,String username,String password) { driver.get("https://passport.cnblogs.com/user/signin?AspxAutoDetectCookieSupport=1"); // 清除input 输入用户名 driver.findElement(By.id("input1")).clear(); driver.findElement(By.id("input1")).sendKeys(username); // 清除input 输入密码 driver.findElement(By.id("input2")).clear(); driver.findElement(By.id("input2")).sendKeys(password); // 点击登录 driver.findElement(By.id("signin")).click(); } public static void logout(WebDriver driver){ // 退出 driver.findElement(By.linkText("退出")).click(); // // 接受弹框 driver.switchTo().alert().accept(); } } 关键字驱动测试实例:(未完待续哈。。。)
4.12 上传文件 4.12.1 sendKeys实现上传 html <html> <head> </head> <body> <div class="row_fluid"> <div class="span10 well"> <h3>Upfile</h3> <input type="file" name="file"/> </div> </div> </body> </html> java代码: package upfile; import java.io.File; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class Upfile { public static void main(String[] args) throws InterruptedException { System.out.println("start"); WebDriver driver = new FirefoxDriver(); File file = new File("C:/Users/Administrator/Desktop/upfile.html"); String filePath = file.getAbsolutePath(); driver.get(filePath); driver.findElement(By.name("file")).sendKeys("D:\\BugReport.txt"); Thread.sleep(2000); driver.close(); } } 4.14 下载文件 package upfile; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; public class Download { public static void main(String[] args) { FirefoxProfile firefox=new FirefoxProfile(); //browser.download.folderList 设置成0代表下载到浏览器默认下载路径,设置成2则可以保存到指定目录。 firefox.setPreference("browser.download.folderList", 2); // browser.download.manager.showWhenStarting 是否显示开始;Ture为显示,Flase为不显示。 firefox.setPreference("browser.download.manager.showWhenStarting", false); //browser.download.dir 用于指定所下载文件的目录。os.getcwd() 函数不需要传递参数,用于返回当前的目录。 firefox.setPreference("browser.download.dir", "d:\\Program Files"); // browser.helperApps.neverAsk.saveToDisk 指定要下载页面的Content-type值,“application/octet-stream”为文件的类型。 // HTTP Content-type常用对照表:http://tool.oschina.net/commons firefox.setPreference("browser.helpApps.neverAsk.saveToDisk", "application/octet-stream"); WebDriver driver=new FirefoxDriver(firefox); driver.get("http://pan.baidu.com/share/link?shareid=3048009203&uk=375774229#list/path=%2F"); driver.findElement(By.xpath(".//*[@id='shareqr']/div[2]/div[2]/div/ul[1]/li[1]/div/span[1]")).click(); driver.findElement(By.xpath(".//*[@id='bd-main']/div/div[1]/div/div[2]/div/div/div[2]/a[2]/span/span")).click(); driver.findElement(By.xpath(".//*[@id='_disk_id_3']/span")).click(); } } 4.15 操作Cookie package com.cy.selenium; import java.util.Set; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class Cookie { public static void main(String[] args) { WebDriver driver=new FirefoxDriver(); driver.get("http://www.baidu.com/"); /*WebDriver操作cookie的方法:: ·getCookies() 获得所有cookie信息。 ·getCookieNamed(String name) 返回字典的key为“name”的cookie信息。 ·addCookie(cookie dict) 添加cookie。“cookie_dict”指字典对象,必须有name 和value 值。 ·deleteCookieNamed(String name) 删除cookie信息。“name”是要删除的cookie的名称;“optionsString”是该cookie的选项,目前支持的选项包括“路径”,“域”。 ·deleteAllCookies() 删除所有cookie信息。 */ Set<org.openqa.selenium.Cookie> coo=driver.manage().getCookies(); System.out.println(coo); } } 4.16 调用JavaScript package com.cy.selenium; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.JavascriptExecutor; public class JavaScript { public static void main(String[] args) throws InterruptedException { System.out.println("==============="); WebDriver driver=new FirefoxDriver(); driver.manage().window().setSize(new Dimension(700, 600)); driver.get("http://www.baidu.com/"); driver.findElement(By.id("kw")).sendKeys("JavaScript"); driver.findElement(By.id("su")).click(); Thread.sleep(2000); // 拖动滚动条 window.scrollTo(左边距,上边距); ((JavascriptExecutor)driver).executeScript("window.scrollTo(100,450);"); Thread.sleep(3000); System.out.println("end"); driver.quit(); } }
4.8 定位一组元素 定位一组元素的方法与定位单个元素的方法类似,唯一的区别是在单词element后面多了一个s表示复数。定位一组元素一般用于以下场景: ·批量操作元素,例如勾选页面上所有的复选框。 ·先获取一组元素,再从这组对象中过滤出需要操作的元素。例如定位出页面上所有的checkbox,然后选择其中的一个进行操作。 eg: 编写一个html页面。 <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <title>checkbox</title> <link href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.main.css" rel="stylesheet"/> <script src="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.main.js"></script> <style> .well{ width:200px; height:300px; margin:40 auto; border:1px solid red; } .control-group{ margin-top:30px; } .controls{ margin-left:100px; margin-top:-15px; } </style> </head> <body> <div class="well"> <h3>checkbox</h3> <form class="form-horizontal"> <div class="control-group"> <label class="control-label" for="c1">checkbox1</label> <div class="controls"> <input type="checkbox" id="c1"/> </div> </div> <div class="control-group"> <label class="control-label" for="c2">checkbox1</label> <div class="controls"> <input type="checkbox" id="c2"/> </div> </div> <div class="control-group"> <label class="control-label" for="c3">checkbox1</label> <div class="controls"> <input type="checkbox" id="c3"/> </div> </div> </form> </div> </body> </html> java代码; package com.cy.selenium; import java.io.File; import java.util.List; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class Test03 { public static void main(String[] args) throws InterruptedException { System.out.println("start"); WebDriver driver = new FirefoxDriver(); File file = new File("C:/Users/Administrator/Desktop/test.html"); String filePath = file.getAbsolutePath(); driver.get(filePath); // 通过css查找一组元素 List<WebElement> inputs = driver.findElements(By.cssSelector("input[type=checkbox]")); for (WebElement checkbox : inputs) { checkbox.click(); } Thread.sleep(3000); // 刷新 driver.navigate().refresh(); // 选择最后一个checkbox 通过xpath查找一组元素 List<WebElement> checkboxs = driver.findElements(By.xpath(".//input[@type='checkbox']")); checkboxs.get(checkboxs.size()-1).click(); Thread.sleep(2000); driver.close(); } } 效果: 4.9 多表单切换 <html> <head> </head> <body> <div class="row_fluid"> <div class="span10 well"> <h3>frame</h3> <iframe id="if" name="nf" src="http://www.baidu.com" width="1200px" height="300px"></iframe> </div> </div> </body> </html> java代码: package com.cy.selenium; import java.io.File; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class Iframe { public static void main(String[] args) throws InterruptedException { System.out.println("start"); WebDriver driver = new FirefoxDriver(); File file = new File("C:/Users/Administrator/Desktop/frame.html"); String filePath = file.getAbsolutePath(); driver.get(filePath); // 切换到iframe (id="if") driver.switchTo().frame("if"); driver.findElement(By.id("kw")).sendKeys("webDrive"); driver.findElement(By.id("su")).click(); Thread.sleep(4000); driver.close(); //退回上一级表单 // driver.switchTo().defaultContent(); } } 4.10 多窗口切换 4.11 警告框处理 在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单,具体做法是使用 switch_to_alert()方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ sendKeys等方法进行操作。 ·getText():返回 alert/confirm/prompt 中的文字信息。 ·accept(): 接受现有警告框。·dismiss():解散现有警告框。·sendKeys(keysToSend): 发送文本至警告框。keysToSend:将文本发送至警告框。 eg: package com.cy.selenium; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.interactions.Actions; public class Alert { public static void main(String[] args) throws InterruptedException { System.out.println("start selenium"); WebDriver driver=new FirefoxDriver();// 用WebDriver new Firefox浏览器的驱动给变量driver,相当于driver拿到了Firefox浏览器的控制权。 driver.get("http://www.baidu.com/"); Actions action=new Actions(driver); // 悬停在设置上 action.clickAndHold(driver.findElement(By.linkText("设置"))).perform(); // 打开搜索设置 driver.findElement(By.className("setpref")).click(); Thread.sleep(3000); //保存设置 driver.findElement(By.className("prefpanelgo")).click(); Thread.sleep(2000); // 接受弹框 driver.switchTo().alert().accept(); Thread.sleep(2000); driver.close(); } }
4.1 从定位元素开始 WebDriver提供了八种元素定位方: 在Java语言中对应的定位方法: ·id findElement(By.id()) ·name findElement(By.name())·class name findElement(By.className())·tag name findElement(By.tagName())·link text findElement(By.linkText())·partial link text findElement(By.partialLinkText())·xpath findElement(By.xpath())·css selector findElement(By.cssSeletor()) id定位:name定位,class定位都是通过他们的属性值来的。 link定位:专门用来定位文本链接。eg:<a class="mnav" name="tj_trnews" href="http://news.baidu.com">新闻</a> 对应的link定位 findElement(By.linkText("新闻"))。 partial link定位:parial link定位是对link定位的一种补充,有些文本链接会比较长,这个时候我们可以取文本链接的一部分定位,只要这一部分信息可以唯一地标识这个链接。eg:<a class="mnav" name="tj_trnews" href="http://news.baidu.com">这是一个很长很长的文本链接</a> 对应的partial link定位 findElement(By.partialLinkText("一个很长的")) 或 findElement(By.partialLinkText("文本链接"))。 xpath定位:最简单的找到元素的位置的方法就是打开FireBug。 CSS定位:<span class="sp"> <input id="abc" name="def" class="ghi" type="text" maxlength='100' /> </span> 通过class属性定位:findElement(By.cssSelector(".ghi")); 通过id属性定位:findElement(By.cssSelector("#abc")); 通过标签名定位:findElement(By.cssSelector("input")); 通过父子关系定位: findElement(By.cssSelector("span > input")); 通过属性定位:findElement(By.cssSelector("input[maxlength='100']")); XPath与CSS的类似功能的简单对例如下表所示 package com.cy.selenium; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.interactions.Actions; import bsh.commands.dir; public class Test01 { public static void main(String[] args) { System.out.println("start selenium"); WebDriver driver=new FirefoxDriver();// 用WebDriver new Firefox浏览器的驱动给变量driver,相当于driver拿到了Firefox浏览器的控制权。 driver.manage().window().maximize(); driver.get("http://www.awbeci.com/"); driver.findElement(By.xpath(".//*[@id='navbar-collapse-01']/ul[2]/li[2]/button")).click(); driver.findElement(By.id("username")).sendKeys("SmileCy"); driver.findElement(By.id("password")).sendKeys("1314520"); driver.findElement(By.linkText("登录")).click(); driver.findElement(By.xpath(".//*[@id='main-container']/div[1]/div[2]/div/div[2]/div/a[3]/span")).click(); // 回车键查询 driver.findElement(By.cssSelector(".form-control")).sendKeys("软件测试学"); new Actions(driver).sendKeys(Keys.ENTER).perform();// 按回车键 // 后退 driver.navigate().back(); // 刷新 driver.navigate().refresh(); // 关闭浏览器 driver.close(); } } 4.2 控制浏览器 4.2.1 控制浏览器窗口大小 driver.manage().window().setSize(new Dimension(400, 500)); 4.2.2 控制浏览器后退、前进 WebDriver也提供了对应的back()和forward()方法来模拟后退和前进按钮 // 后退 driver.navigate().back(); // 刷新 driver.navigate().refresh(); 4.2.3 模拟浏览器刷新 driver.navigate().refresh(); 4.3 简单元素操作 ·clear() 清除文本。 ·sendKeys(*value) 模拟按键输入。 ·click() 单击元素。 4.3.2 WebElement接口常用方法 submit() submit()方法用于提交表单 getSize() 返回元素的尺寸。 getText() 获取元素的文本。 getAttribute(name) 获得属性值。 isDisplayed() 设置该元素是否用户可见。 4.4 鼠标事件 Actions 类提供了鼠标操作的常用方法:· contextClick() 右击· clickAndHold() 鼠标点击并控制· doubleClick() 双击· dragAndDrop() 拖动· release() 释放鼠标 · perform() 执行所有Actions中存储的行为 鼠标右击操作。 导入提供鼠标操作的ActionChains 类Actions(driver)调用Actions()类,将浏览器驱动driver作为参数传入。contexClick(xxx)contextClick()方法用于模拟鼠标右键操作,在调用时需要指定元素定位。perform() 执行所有ActionChains中存储的行为,可以理解成是对整个操作的提交动作。 action.contextClick(driver.findElement(By.className("cnisdisd"))).perform(); 鼠标悬停。clickAndHold(); action1.clickAndHold(driver.findElement(By.linkText("设置"))).perform(); 鼠标双击操作。doubleClick()方法用于模拟鼠标双击操作。 action1.doubleClick(driver.findElement(By.className(""))).perform(); 鼠标拖放操作。drag_and_drop(source, target)在源元素上按下鼠标左键,然后移动到目标元素上释放。·source:鼠标拖动的源元素。·target:鼠标释放的目标元素。 WebElement source=driver.findElement(By.className("element")); WebElement target=driver.findElement(By.className("element")); action1.dragAndDrop(source, target).perform(); 释放鼠标release()方法用于释放鼠标事件。 action1.release().perform(); 4.5 键盘事件 使用键盘按键方法前需要先导入keys类。以下为常用的键盘操作:sendKeys(Keys.BACK_SPACE) 删除键(BackSpace)sendKeys(Keys.SPACE) 空格键(Space)sendKeys(Keys.TAB) 制表键(Tab)sendKeys(Keys.ESCAPE) 回退键(Esc)sendKeys(Keys.ENTER) 回车键(Enter)sendKeys(Keys.CONTROL,'a') 全选(Ctrl+A) sendKeys(Keys.CONTROL,'c') 复制(Ctrl+C)sendKeys(Keys.CONTROL,'x') 剪切(Ctrl+X)sendKeys(Keys.CONTROL,'v') 粘贴(Ctrl+V)sendKeys(Keys.F1) 键盘F1……sendKeys(Keys.F12) 键盘F12 package com.cy.selenium; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class Test02 { public static void main(String[] args) { WebDriver driver=new FirefoxDriver(); driver.manage().window().maximize(); driver.get("http://www.baidu.com/"); WebElement input =driver.findElement(By.id("kw")); // 输入内容 input.sendKeys("seleniumm"); // 删除多余的m input.sendKeys(Keys.BACK_SPACE); // 输入空格+教程 input.sendKeys(Keys.SPACE); input.sendKeys("教程"); // ctrl+a input.sendKeys(Keys.CONTROL,"a"); // ctrl+x input.sendKeys(Keys.CONTROL,"x"); // ctrl+v input.sendKeys(Keys.CONTROL,"v"); // 通过回车键 查询 input.sendKeys(Keys.ENTER); driver.close(); } } 4.6 获得验证信息 getTitle():用于获得当前页面的title。getCurrentUrl() :用户获得当前页面的URL。 4.7 设置元素等待 4.7.1 timeouts方法 WebDriver提供了几种方法来等待元素。 ·implicitlyWait。识别对象时的超时时间。过了这个时间如果对象还没找到的话就会抛出NoSuchElement异常。·setScriptTimeout。异步脚本的超时时间。WebDriver可以异步执行脚本,这个是设置异步执行脚本脚本返回结果的超时时间。·pageLoadTimeout。页面加载时的超时时间。因为WebDriver会等页面加载完毕再进行后面的操作,所以如果页面超过设置时间依然没有加载完成,那么WebDriver就会抛出异常 4.7.2 sleep休眠方法 Thread.sleep()方法 当执行到sleep()方法时会固定的休眠所设置的时长(这里以毫秒为单位);然后再继续执行。注意,这里的Thread.sleep()方法不能直接使用,必须加上异常的处理。throws关键字通常被应用在声明方法时,用来指定可能抛出的异常。 package com.cy.selenium; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class Wait { public static void main(String[] args) { WebDriver driver=new FirefoxDriver(); driver.manage().window().maximize(); //页面加载超时时间为5s driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS); driver.get("http://www.baidu.com/"); //定位对象时间5s,还没订到位置报异常 driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); driver.findElement(By.id("kww")).sendKeys("selenium"); // Exception in thread "main" org.openqa.selenium.NoSuchElementException: Unable to locate element: {"method":"id","selector":"kww"} //异步脚本的超时时间为5s driver.manage().timeouts().setScriptTimeout(5, TimeUnit.SECONDS); // Thread.sleep 需要异常处理 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } driver.close(); } }
2.1 Window下环境搭建 2.1.1 安装Java 2.1.2 安装Eclipse (网上资源很多,就不详将了)。 2.1.3 下载Java版的Selenium包。 下载地址:http://docs.seleniumhq.org/download/ 提供一下百度网盘下载地址: http://pan.baidu.com/share/link?shareid=1233226792&uk=375774229 2.1.4 创建第一个Java程序 eclipse导入selenium的所有jar包。 简单步骤“ ——》 ——》——》——》 2.2 编写第一个自动化脚本 package com.cy.selenium; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; public class Test01 { public static void main(String[] args) { System.out.println("start selenium"); WebDriver driver=new FirefoxDriver();// 用WebDriver new Firefox浏览器的驱动给变量driver,相当于driver拿到了Firefox浏览器的控制权。 // driver.manage().window().maximize(); driver.get("https://www.baidu.com/"); driver.findElement(By.id("kw")).sendKeys("selenium2 java"); driver.findElement(By.id("su")).click(); try { driver.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); }finally { driver.close(); } } } 运行这段代码,就会出现效果了。(Fiefox浏览器默认安装到C盘)。 还有一些问题可能是你的浏览器不兼容的问题。 这里我的selenium2.45,火狐使用的是36的版本。 浏览器没有安装在C盘的话,使用 System的setProperty()方法指定浏览器的路径,eg: System.setProperty("webdriver.firefox.bin", "D:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"); 我本来就是学习java的。
在博客园遇到一位朋友,她将我的代码下载下去,运行的时候,jsp页面的样式不存在,不会显示出来。 这里就将我之前写的SpringSpringmvcMybatis做一些修改。 jdk1.8 加入了两个jar包:jstl-1.0.4.jar,taglibs-standard-jstlel-1.2.5.jar。 分享包:http://pan.baidu.com/share/link?shareid=3048009203&uk=375774229 主要就是要引入静态的资源: 在mvc-servlet.xml里配置(你需要使用到的一些静态资源) <mvc:resources location="static/css/" mapping="static/css/**" /> <mvc:resources location="static/images/" mapping="static/images/**" /> 在jsp页面中的静态资源的路径前加上 <%=basePath%> eg: <link rel="stylesheet" type="text/css" href="<%=basePath%>static/css/style.css"> 我把静态资源配置了,样式的显示出来了。
都是一些最基础的知识点。 一:软件测试分类 1)单元测试:单元测试(或模块测试)是对程序中的单个子程序或具有独立功能的代码段进行测试的过程。2)集成测试:集成测试是在单元测试的基础上,先通过单元模块组装成系统或子系统,再进行测试。重点是检查模块之间的接口是否正确。3)系统测试:系统测试是针对整个产品系统进行的测试,验证系统是否满足需求规格的定义,以及软件系统的正确性和性能等是否满足其需求规格的要求。 4)验收测试:验收测试是部署软件之前的最后一个测试阶段。验收测试的目的是确保软件准备就绪,向软件购买者展示该软件系统能够满足用户的需求。 1)黑盒测试。黑盒测试,指的是把被测的软件看作一个黑盒子,我们不去关心盒子里面的结构是什么样子的,只关心软件的输入数据和输出结果。(黑盒测试着眼于程序外部结构,不考虑内部逻辑结构,主要针对软件界面和软件功能进行测试。) 2)白盒测试。白盒测试,指的是把盒子打开,去研究里面的源代码和程序执行结果。(检验程序中的每条逻辑路径是否都能按预定要求正确工作。) 3)灰盒测试。灰盒测试介于黑盒测试与白盒测试之间。(既关注输出对于输入的正确性,同时也关注内部表现。) 功能测试与性能测试 1)功能测试。(逻辑功能测试、界面测试、易用性测试、安装测试、兼容性测试等。) 2)性能测试。(时间性能和空间性) 手工测试与自动化测试 2)自动化测试。(功能自动化测试与性能自动化测试) 功能自动化测试:它是把以人为驱动的测试行为转化为机器执行的一种过程。通过测试工具(或框架)录制/编写测试脚本,对软件的功能进行测试,并验证测试结果是否正确,从而代替部分的手工测试工作,达到节约人力成本和时间成本的目的。 性能自动化测试:通过性能工具来模拟成千上万的虚拟用户向系统发送请求,从而来验证系统的处理能力。从而来验证系统的处理能力。 二:分层的自动化测试 单元自动化测试 (单元测试是指对软件中的最小可测试单元进行检查和验证。) 接口自动化测试 (模块接口测试和Web接口测试。) (1)模块接口测试,主要测试模块之间的调用与返回。 (2)Web接口测试又可分为两类:服务器接口测试和外部接口测试。 ·服务器接口测试:指测试浏览器与服务器的接口。 ·外部接口测试:指调用的接口由第三方系统提供。(第三方登录) (接口测试也有相应的类库或工具,例如测试HTTP的有HttpUnit、Postman等。) UI自动化测试 UI层是用户使用该产品的入口,所有功能都通过这一层提供并展示给用户,所以大多测试工作都集中在这一层进行。(目前主流的测试工具有UFT、Watir、Robot Framework、Selenium等。) 三:什么样的项目适合自动化测试 1)软件需求变动不频繁。 2)项目周期较长。 3)自动化测试脚本可重复使用。 四:自动化测试及工具的简述 1)UFT。 UFT(全称Unified Functional Testing)由QTP(Quick Test Professional software)与ST(Service Test)合并而来,由HP公司开发。它是一种企业级的自动测试工具,提供了强大易用的录制回放功能,同时兼容对象识别模式与图像识别模式两种识别方式,支持B/S 与C/S 两种架构的软件测试,是目前主流的自动化测试工具。2)Robot Framework。Robot Framework是一款基于Python语言编写的自动化测试框架,具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可以进行分布式测试。3)Watir。Watir全称是“Web Application Testing in Ruby”,是一种基于Web模式的自动化功能测试工具。Watir是一个Ruby语言库,使用Ruby 语言进行脚本开发。4)Selenium。Selenium也是一个用于Web应用程序测试的工具,支持多平台、多浏览器、多语言去实现自动化测试。目前在Web自动化领域应用越来越广泛。
样式: 主要提供右键功能代码。 (只需要提供你需要的js和css就行了) <!doctype html> <html> <head> <base href="/smile/" /> <title>标签右键菜单</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta HTTP-EQUIV="Pragma" CONTENT="no-cache"> <meta HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> <meta HTTP-EQUIV="Expires" CONTENT="0"> <link rel="stylesheet" type="text/css" href="/smile/js/easyui/themes/icon.css?ver=1.0031" /> <link rel="stylesheet" type="text/css" href="/smile/js/easyui/themes/gray/easyui.css?ver=1.0031" /> <script type="text/javascript" src="/smile/js/jquery.min.js?ver=1.0031"></script> <script type="text/javascript" src="/smile/js/easyui/jquery.easyui.min.js?ver=1.0031"></script> </head> <body class="easyui-layout"> <div data-options="region:'west',title:'功能导航菜单',collapsible:false,iconCls:'fa fa-leaf'" style="width: 200px;"> <div id="aa" class="easyui-accordion" data-options="fit:true"> <div title="菜单一" style="overflow: auto; padding: 5px;" iconCls="fa fa-building"> <ul class="easyui-tree" data-options="onClick:clickTree"> <li data-options="attributes:{'url':'/smile/html/menu1.html'}"><span>菜单一</span></li> </ul> </div> <div title="菜单二" style="overflow: auto; padding: 5px;" iconCls="fa fa-building"> <ul class="easyui-tree" data-options="onClick:clickTree"> <li data-options="attributes:{'url':'/smile/html/menu2.html'}"><span>菜单二</span></li> </ul> </div> </div> </div> <!--首页 --> <div data-options="region:'center',collapsible:false"> <div id="mainTabs" class="easyui-tabs" data-options="fit:true,narrow:true"> <div title="首页" style="overflow:hidden;" data-options="iconCls:'fa fa-home'"> <div id="myclock" style="margin: 0 auto;width: 400px;" class="clock"></div> </div> </div> </div> <!--右键菜单栏 --> <div id="mm" class="easyui-menu" style="width: 120px;"> <div id="mm-tabclosecurrent" name="1"> 关闭当前页</div> <div id="mm-tabcloseall" name="2"> 全部关闭</div> <div id="mm-tabcloseother" name="3"> 其他全部关闭</div> </div> <script type="text/javascript"> $(function(){ //监听右键事件,创建右键菜单 $('#mainTabs').tabs({ onContextMenu:function(e, title,index){ e.preventDefault(); if(index>0){ $('#mm').menu('show', { left: e.pageX, top: e.pageY }).data("tabTitle", title); } } }); //右键菜单click $("#mm").menu({ onClick : function (item) { closeTab(this, item.name); } }); }); function addTab(title, url) { if ($('#mainTabs').tabs('exists', title)) { $('#mainTabs').tabs('select', title); } else { var content = '<iframe scrolling="auto" frameborder="0" src="'+ url+'" style="width:100%;height:99%;"></iframe>'; $('#mainTabs').tabs('add', { title: title, content: content, closable: true , cache:true, }); } } // //删除Tabs function closeTab(menu, type) { var allTabs = $("#mainTabs").tabs('tabs'); var allTabtitle = []; $.each(allTabs, function (i, n) { var opt = $(n).panel('options'); if (opt.closable) allTabtitle.push(opt.title); }); var curTabTitle = $(menu).data("tabTitle"); var curTabIndex = $("#mainTabs").tabs("getTabIndex", $("#mainTabs").tabs("getTab", curTabTitle)); switch (type) { case "1"://关闭当前 $("#mainTabs").tabs("close", curTabTitle); return false; break; case "2"://全部关闭 for (var i = 0; i < allTabtitle.length; i++) { $('#mainTabs').tabs('close', allTabtitle[i]); } break; case "3"://关闭其他全部 for (var i = 0; i < allTabtitle.length; i++) { if (curTabTitle != allTabtitle[i]) $('#mainTabs').tabs('close', allTabtitle[i]); } $('#mainTabs').tabs('select', curTabTitle); break; } } function clickTree(node) { if ($(this).tree('isLeaf', node.target)) { addTab(node.text, node.attributes.url); } else { $(this).tree('toggle', node.target); } } </script> </body> </html>
之前一直不知道,easyui 的combobox还有从左匹配查询显示数据的。 样式是这样的:(这是数据是已经存在下拉列表里的) 在这样操作的时候,遇到了一个问题。(其实也不算问题的)。 就是操作人员在添加机构的时候,把机构的名称打了空格,就是相当于是这样的一个字符串 " XXX机构"。 这样的话,在combobox里左匹配是查询不出这条数据的。 但是这条数据时存在这个下来列表里的!
一 处理重复数据 1 使用 UNIQUE 唯一键 (添加数据) 创建表的时候设置 2 使用 DISTINCT (查询数据--过滤) eg:SELECT DISTINCT `name` from star 3 统计重复的数据 eg: SELECT count(*) number,`name` FROM star GROUP BY `name` HAVING number>1 (统计姓名重复的数据) 三 拼接串 使用CONCAT() 1 SELECT CONCAT(name,'(',age,')') FROM student RTRIM() 删除右侧多余空格来整理数据 LTRIM() 删除左侧多余空格来整理数据 TRIM() 删除左右两侧的空格 四 时间函数 (一般用于创建数据的时候自动生成的时间) DATE() 主要是日期 TIME() 主要是时间 NOW() 获得当前时间 五 数值处理函数 ABS() 返回一个数的绝对值 COS() 返回一个角度的余弦值 EXP() 返回一个数的指数值 MOD() 返回除操作的余数 六 PI() 返回圆周率 RAND() 返回一个随机数 SIN() 返回一个角度的正弦 SQRT() 返回一个数的平方根 TAN() 返回一个角度的正切 七 聚集函数 AVG() 返回某列的平均值 COUNT() 返回某列的行数 MAX() 返回某列的最大值 MIN() 返回某列的最小值 SUM() 返回某列值之和,忽略列值为NULL的行
在项目中几次都遇到了同样的问题,现在都不知道怎样解决了! 路过的朋友们帮我看看嘛!谢谢了! 最后我想要实现的效果是这样的。 在下拉列表中不存在值。(这里的是下拉列表中存在值的!) 但是在我输入值的时候可以左匹配模糊查询。(我要在输入框里一直可以输入值)。 但是现在的问题是这样的。 下拉列表不存在值,这是我想要的。 但是 我这个是在输入框里,我已经输入了 苏州 两个字的,但是下拉列表出现值的时候,我的输入框里就不存在值了。而且也不可以输入值了。 在这里的写法是这样的: html: <tr> <td style="width: 110px;">地址名称</td> <td> <input class="easyui-combobox" name="name" id="name" style="width:200px" data-options="required:true,validType:'length[0,128]' "/> </td> </tr> js:(当我在输入框里输入值的时候) $('#name').combobox({ onChange:function(newValue,oldValue){ $.ajax({ type:'get', url:'..../sysCodeRecods/'+newValue+'/listName?time='+new Date().getTime(), success: function(data){ if(data.data){ var r = data.data.rows;rowslen = r.length; for(var i = 0; i < rowslen; i++){ $("#name").append("<option value="+r[i].code_value+">"+r[i].code_name+"</option>"); } $("#name").combobox({}); } }, }) }, }); 数据时查出来了。 但是下拉列表有值的时候,输入框里就不存在值了,再想输入值都输不进去了! 输入的地址名称,可能是存在码表里的,我就需要从码表里去模糊匹配值,这个码表里数据太多了,我不可能先加载了数据,放在combobox里,再去模糊匹配。 脑袋都大了,我都不知道怎么处理了! 朋友们帮帮看看嘛!
视频地址(第六课时):https://pan.baidu.com/s/1gfLVC2n 资源: 用于存放自己的关键字
在项目中一直存在一个问题,一直都没发现问题的根源在哪里。在我们测试这边也是偶尔会出现。但是每次我去测试的时候也没问题。今天终于找到原因了! 在easyui中点击行和点击复选框触发的事件是不一样的! 点击行触发的事件 项目中部分代码:: onClickRow:function(index, row){ hospnewId=row.id; hosName=row.name; } 当我点击复选框的时候就不会触发这个事件,所以就得不到我想要的值。 看了一下easyui的api,发现有两个事件(可以满足我想要的效果): onSelect:选中那一行。 onCheck:未勾选不会触发onCheck,check是开始的一列checkbox。 之前我还一直不知道是怎么搞的,还以为是我的环境配置问题,浏览器的问题!现在问题终于解决了!
视频地址(第五课时):https://pan.baidu.com/s/1gfLVC2n alert 弹出窗口 Alert Should Be Present Get Alert Message confirm 弹出窗口 Choose Cancel On Next Confirmation Choose Ok On Next Confirmation Confirm Action AutoIt Libary
视频地址(第四课时):https://pan.baidu.com/s/1gfLVC2n 浏览器交互的关键字: Selenium2Library (搜索关键字 --F5) Open Browser :(打开浏览器) Name: Open Browser Source: Selenium2Library <test library> Arguments: [ url | browser=firefox | alias=None | remote_url=False | desired_capabilities=None | ff_profile_dir=None ]; Maximize Browser Windown,Go To,Go Back,Reload Page,Close Window,Close Browser,Close All Browser 在这里面有详细的介绍。在这就不一一介绍了! 驱动不同的浏览器(默认是火狐) 需要去下载不同浏览器的driver。 常用关键字 1) 点击 Click Button | locator (点击按钮) Click Element | locator (点击元素) Click Image | locator (点击图片) Click Link | locator (点击链接) Double Click Element | locator (双击元素) 注意:有时候在IE中,Click不起作用,可以用Press Key | <id> | \\13或\\10 来代替。 2) 输入 Input Text | locator |text Input Password | locator |text 3)和其他界面元素交互的关键词 Element (元素) Focus 聚焦 Simulate 模拟时间触发 Open Context Menu 打开 List (列表) Select All From List Slect From List Unselect From List Checkbox (复选框) Select Checkbox Unselect Checkbox Radio Button (单选按钮) Select Radio Button From (表单) Submit Choose File (For File Upload) 获取页面信息 Page Get Title Get Location Get Source Get Matching Xpath Element Get Element Attribute Get Value Get Text Link Get All Links 练习的话 可以登录到你的博客,写入标题,内容,个人分类,移动随笔,发布!
视频地址(第三课时):https://pan.baidu.com/s/1gfLVC2n 元素定位 Selenium支持的定位方式: id, name,xpath,dom,link,css,jquery,tag.(我们通常是使用id或name)。 当没有id的时候我们可以Assign Id To Element关键字来为元素分配一个id。 注意:有时需要先进入一个frame或iframe再定位一个元素的,我们需要使用两个关键字, 进入一个frame或iframe : Sleect Frame | locator 回到最上层frame或iframe: Unselect Frame XPath :XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。 了解更多关于XPath的信息 :http://www.w3school.com.cn/xpath/index.asp XPath的定位方法:绝对定位,相对定位,索引定位,属性值定位,属性名称定位,部分属性值定位,使用至来匹配任意属性及元素。 找个地址来练习练习就差不多了!
视频地址(第二课时):https://pan.baidu.com/s/1gfLVC2n 软件安装好了! 软件默认的浏览器是火狐。 如果需要IE,chrome,都在前一篇的安装包里有。 测试结果 视频里讲的比较详细的!这里查找用了一些Robot Framework的 快捷键! 重命名——》F2 搜索关键字——》F5 执行用例——》F8 创建新工程——》ctrl+n 创建新测试套——》ctrl+shift+f 创建新用例——》ctrl+shift+t 创建新关键字——》ctrl+shift+k 向上移动用例——》ctrl+↑ 向下移动用例——》ctrl+↓ 显示关键字信息——》 ctrl+鼠标悬浮(鼠标悬浮于关键字上) 自动补全关键字——》ctrl+shift+空格 删除行——》ctrl+d 删除单元格——》ctrl+shift+d 插入单元格——》ctrl+shift+i 插入行——》ctrl+i 屏蔽代码——》ctrl+# 取消屏蔽——》ctrl+$ 保存整个工程——》ctrl+shit+s 局部保存,保存鼠标点击的部分——》ctrl+s 查看log——》ctrl+L 查看report——》ctrl+r
突然有一天 看见我们公司的搞测试,写了一个自动化的从测试,我就感觉好棒的,所以我也来学习学习。也在网上找了一些资源。 主要来自这个人的博客:http://blog.csdn.net/xc5683/article/category/1263335 非常感谢! 今天主要就是安装软件。 首先需要的包。 安装的步骤跟着视频来就好了! 包的地址:https://pan.baidu.com/s/1hsNp3bY 视频地址:https://pan.baidu.com/s/1o8FBGSE
1 #是将传入的值当做字符串的形式,eg:select id,name,age from student where id =#{id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id ='1'. 2 $是将传入的数据直接显示生成sql语句,eg:select id,name,age from student where id =${id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1. 3 使用#可以很大程度上防止sql注入。(语句的拼接) 4 但是如果使用在order by 中就需要使用 $. 5 在大多数情况下还是经常使用#,但在不同情况下必须使用$. 我觉得#与$的区别最大在于:#{} 传入值时,sql解析时,参数是带引号的,而${}穿入值,sql解析时,参数是不带引号的。 一 : 理解mybatis中 $与# 在mybatis中的$与#都是在sql中动态的传入参数。 eg:select id,name,age from student where name=#{name} 这个name是动态的,可变的。当你传入什么样的值,就会根据你传入的值执行sql语句。 二:使用$与# #{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符 。 ${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。 name-->cy eg: select id,name,age from student where name=#{name} -- name='cy' select id,name,age from student where name=${name} -- name=cy
需要下载一个poi的jar包。 控制器 @Override public void getContractListExecl(Contract contract, BindingResult result, HttpServletRequest req, HttpServletResponse response) throws IOException { List<Map<String, Object>> sttList = contractService.getContracts(contract);// 得到需要导出的数据 int i = 0; // 第一步,创建一个webbook,对应一个Excel文件 @SuppressWarnings("resource") HSSFWorkbook wb = new HSSFWorkbook(); // 第二步,在webbook中添加一个sheet,对应Excel文件中的sheet HSSFSheet sheet = wb.createSheet("title"); // 设置表格默认列宽度为15个字节 sheet.setDefaultColumnWidth((short) 20); // 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short HSSFRow row = sheet.createRow(i); // 第四步,创建单元格,并设置值表头 设置表头居中 HSSFCellStyle style = wb.createCellStyle(); style.setAlignment(HSSFCellStyle.ALIGN_LEFT); // 创建一个居中格式 HSSFCell cell = row.createCell(0); cell.setCellValue("用户姓名"); cell.setCellStyle(style); cell = row.createCell(1); cell.setCellValue("电话"); cell.setCellStyle(style); cell = row.createCell(2); cell.setCellValue("家庭医生团队名称"); cell.setCellStyle(style); cell = row.createCell(3); cell.setCellValue("服务办理人姓名"); cell.setCellStyle(style); cell = row.createCell(4); cell.setCellValue("服务办理人电话"); cell.setCellStyle(style); cell = row.createCell(5); cell.setCellValue("服务办理人代码"); cell.setCellStyle(style); cell = row.createCell(6); cell.setCellValue("完成签约时间"); cell.setCellStyle(style); cell = row.createCell(7); cell.setCellValue("服务完成ITV编码"); cell.setCellStyle(style); cell = row.createCell(8); cell.setCellValue("服务完成时间"); cell.setCellStyle(style); cell = row.createCell(9); for (Map<String, Object> row_ : sttList) { row = sheet.createRow(++i); // 第四步,创建单元格,并设置值 row.createCell(0) .setCellValue(row_.get("userName") == null ? "###" : row_.get("userName").toString()); row.createCell(1) .setCellValue(row_.get("userPhone") == null ? "###" : row_.get("userPhone").toString()); row.createCell(2).setCellValue(row_.get("dgName") == null ? "###" : row_.get("dgName").toString()); row.createCell(3) .setCellValue(row_.get("proxyName") == null ? "###" : row_.get("proxyName").toString()); row.createCell(4).setCellValue(row_.get("proxyPhone") == null ? "###" : row_.get("proxyPhone").toString()); row.createCell(5).setCellValue(row_.get("proxyCode") == null ? "###" : row_.get("proxyCode").toString()); row.createCell(6).setCellValue(row_.get("updateTime") == null ? "###" : row_.get("updateTime").toString()); row.createCell(7).setCellValue(row_.get("itvToken") == null ? "###" : row_.get("itvToken").toString()); row.createCell(8).setCellValue(row_.get("itvUpdateTime") == null ? "###" : row_.get("itvUpdateTime").toString()); } String fileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "ISO8859-1") + ".xls"); OutputStream fout = response.getOutputStream(); wb.write(fout); fout.close(); } } 前端调用该接口就可以了! 效果图:
在项目中有些地方需要批量添加的数据,在这里使用到checkbox比较方便一些。 例如:我需要将多个包添加到同一个地区的 主要的前端代码: 这个是用来放checkbox的。 <div id="servicePacks"></div> 通过ajax来得到数据库的数据,循环写出checkbox把数据绑上去。 function getAllServicePacks(){ $.ajax({ method: 'get', url: '路径', success: function (back) { if (back["stateCode"] === 200 && back.data && back.data.rows) { var rows = back.data.rows, len = rows.length, checkbox=$('#servicePacks'); for (var i = 0; i < len; ++i) { checkbox.append($("<input type='checkbox' class='check_box' value='" + rows[i].id + "' name='jbbm' />&nbsp;&nbsp;<span>" + rows[i].servicePackName + "</span><br/><br/>")); } } } }) } 这里的得到选中的数据: var qx = $("input[name='jbbm']:checked").map(function () { return $(this).val(); }).get().join(','); 在后台只需要用split分割就好了!
想要远程登录必须具备几个条件: IP地址,被远程的服务器是否允许远程访问,服务器帐户名,密码。 打开运行的窗口 输入 出现 输入IP地址, 进去后 输入用户名,密码就可以实现远程了!
今天在测试项目的时候 突然就报了一个错出来。 User does not have access to metadata required to determine stored procedure parameter types. If rights can not be granted, configure connection with "noAccessToProcedureBodies=true" to have driver generate parameters that represent INOUT strings irregardless of actual parameter types. 简单说明一下就是 : JDBC在调用存储过程时不光用户要有execute的权限,还需要对mysql.proc具有访问权限。否则它无法访问metadata。 经过查找资料发现有两种方式可以解决: 一:给数据库连接设置一个noAccessToProcedureBodies属性,属性值为true eg: jdbc:mysql://192.168.1.111:3306/test?noAccessToProcedureBodies=true 网上说设置noAccessToProcedureBodies=true会带来一些影响(未经考证): 1. 调用存储过程时,将没有类型检查,设为字符串类型,并且所有的参数设为int类型,但是在调用registerOutParameter时,不抛出异常。 2. 存储过程的查询结果无法使用getXXX(String parameterName)的形式获取,只能通过getXXX(int parameterIndex)的方式获取。 二:给数据库用户赋权,赋执行mysql.proc表的select权限 eg: GRANT SELECT ON mysql.proc TO 'user'@'localhost';
var width = $(window).width(); var height = $(window).height(); var html = "<div id='loading' style='position:absolute;left:0;width:100%;height:" + height + "px;top:0;background:#EFEFEF;opacity:1;filter:alpha(opacity=100);'>"; html += "<div style='position:absolute;cursor1:wait;left:" + ((width / 2) - 75) + "px;top:200px;width:150px;height:16px;padding:12px 5px 10px 30px;"; html += "background:#EFEFEF url(/yxt-admin/js/easyui/themes/gray/images/loading.gif) no-repeat scroll 5px 10px;border:2px solid #ccc;color:#000;'>"; html += "正在加载,请等待..."; html += "</div>"; html += "</div>"; window.onload = function () { var mask = document.getElementById('loading'); mask.parentNode.removeChild(mask); }; document.write(html); 加上这段代码就好了!
昨天画了圆形,判marker是否存在圆形内。今天来画多边形,判断marker在多边形内。 需要引入一个js <script type="text/javascript" src="http://api.map.baidu.com/library/GeoUtils/1.2/src/GeoUtils.js"></script> 百度地图API覆盖物多边形类 http://developer.baidu.com/map/reference/index.php?title=Class:%E8%A6%86%E7%9B%96%E7%89%A9%E7%B1%BB/Polygon http://developer.baidu.com/map/reference/index.php?title=Class:%E8%A6%86%E7%9B%96%E7%89%A9%E7%B1%BB/PolygonOptions 和前一篇差不多的,改变的地方就是画矩形,判断点在矩形内的js; // 画可编辑矩形 var mPoint=new BMap.Point(e.lng,e.lat);// 得到中心点坐标 var pStart = new BMap.Point(e.lng,e.lat); var pEnd = new BMap.Point((e.lng+0.1),(e.lat+0.1)); var polygon = new BMap.Polygon([ new BMap.Point(pStart.lng,pStart.lat), new BMap.Point(pEnd.lng,pStart.lat), new BMap.Point(pEnd.lng,pEnd.lat), new BMap.Point(pStart.lng,pEnd.lat) ], {strokeColor:"blue", strokeWeight:1, strokeOpacity:0.5}); map.addOverlay(polygon); polygon.enableEditing(); 判断点在多边形内 for(var i=0;i<overlays.length;i++){ //判断 覆盖物为标注的并且是在多边形区域内部的 if(overlays[i].uQ == "Marker" && overlays[i].id){ //判断marker是否在多边形内 if(BMapLib.GeoUtils.isPointInPolygon(overlays[i].getPosition(), polygon)){ maker_arr.push(overlays[i]); maker_id.push(overlays[i].id); } } }
把下面这段代码复制到百度地图的demo中运行,效果就是我想设计的效果。 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> body, html {width: 100%;height: 100%;margin:0;font-family:"微软雅黑";} #allmap{width:100%;height:500px;} p{margin-left:5px; font-size:14px;} </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script> <title>圆形区域搜索</title> </head> <body> <div id="allmap"></div> <p>返回北京市地图上圆形覆盖范围内的“餐馆”检索结果,并展示在地图上</p> </body> </html> <script type="text/javascript"> // 百度地图API功能 var map = new BMap.Map("allmap"); // 创建Map实例 var mPoint = new BMap.Point(116.404, 39.915); map.enableScrollWheelZoom(); map.centerAndZoom(mPoint,15); var circle = new BMap.Circle(mPoint,1000,{fillColor:"blue", strokeWeight: 1 ,fillOpacity: 0.3, strokeOpacity: 0.3,enableEditing:true}); map.addOverlay(circle); var removecircle = function(e,ee,marker){ //取得地图上所有的覆盖物 var overlays = map.getOverlays(); //取得圆形区域 var bounds = marker.getBounds(); var maker_arr = []; for(var i=0;i<overlays.length;i++){ //判断 覆盖物为标注的并且是在圆形区域内部的 if(overlays[i].uQ == "Marker"){ //获取标注点到圆心的距离 与半径做对比 if(map.getDistance(marker.getCenter(),overlays[i].getPosition()) < marker.getRadius()){ maker_arr.push(overlays[i]); } } } var r=confirm("你确定要删除区域中"+(maker_arr.length-1)+"个标注吗?"); if (r==true){ for(var i=0;i<maker_arr.length;i++){ map.removeOverlay(maker_arr[i]); } map.removeOverlay(marker); }else{ map.removeOverlay(marker); } } //创建右键菜单 var circleMenu=new BMap.ContextMenu(); circleMenu.addItem(new BMap.MenuItem('删除',removecircle.bind(removecircle))); circle.addContextMenu(circleMenu); var local = new BMap.LocalSearch(map, {renderOptions: {map: map, autoViewport: false}}); //local.searchNearby('餐馆',mPoint,1000); function addMarker(point){ var marker = new BMap.Marker(point); map.addOverlay(marker); } // 随机向地图添加25个标注 var bounds = map.getBounds(); var sw = bounds.getSouthWest(); var ne = bounds.getNorthEast(); var lngSpan = Math.abs(sw.lng - ne.lng); var latSpan = Math.abs(ne.lat - sw.lat); for (var i = 0; i < 5; i ++) { var point = new BMap.Point(sw.lng + lngSpan * (Math.random() * 0.7), ne.lat - latSpan * (Math.random() * 0.7)); addMarker(point); } </script> 功能:以某个区域批量删除maker。 功能的设计思路: 1 右键菜单--》区域删除机构。 2 先画圆(以鼠标点击的某个点为中心点 画圆)。 3 得到圆中所选中的标注(主键)。 4 圆上右键删除 4 .1 确定 删除圆,删除标注(连接数据库数据)。 4.2 取消 删除圆。 主要功能代码(js); 百度地图API:Class:覆盖物类/Circle http://developer.baidu.com/map/reference/index.php?title=Class:%E8%A6%86%E7%9B%96%E7%89%A9%E7%B1%BB/Circle { text:'区域删除机构', callback:function(e){ var mPoint=new BMap.Point(e.lng,e.lat);// 得到中心点坐标 // 一句js画圆 但是这个不可以重复画圆 需要刷新页面后才可以重新画圆 /* var circle = new BMap.Circle(mPoint,1000,{fillColor:"blue", strokeWeight: 1 ,fillOpacity: 0.3, strokeOpacity: 0.3,enableEditing:true}); map.addOverlay(circle); */ var circle = new BMap.Circle(mPoint,5000); circle.setFillColor("blue"); //填充颜色 circle.setStrokeWeight(1);// 设置圆形边线的宽度,取值为大于等于1的整数。 circle.setFillOpacity(0.3);// 返回圆形的填充透明度。 circle.setStrokeOpacity(0.3);// 设置圆形的边线透明度,取值范围0 - 1。 // 这样画圆 可编辑的圆 这两句js代码的位置不可改变 map.addOverlay(circle);// 把圆添加到地图中 circle.enableEditing();// 设置可编辑的圆 var removecircle = function(e,ee,marker){ var deleteid=[]; var idd=[]; //取得地图上所有的覆盖物 var overlays = map.getOverlays(); //取得圆形区域 var bounds = marker.getBounds(); var maker_arr = []; var maker_id =[]; for(var i=0;i<overlays.length;i++){ //判断 覆盖物为标注的并且是在圆形区域内部的 if(overlays[i].uQ == "Marker" && overlays[i].id){ //获取标注点到圆心的距离 与半径做对比 if(map.getDistance(marker.getCenter(),overlays[i].getPosition()) < marker.getRadius()){ maker_arr.push(overlays[i]); maker_id.push(overlays[i].id);// 标注主键值 } } } $.messager.confirm('确认','你确定要删除区域中选中的标注吗?',function(r){ if (r==true){ map.removeOverlay(circle)// 删除圆 for(var i=0;i<maker_id.length;i++){ $.ajax({ type: 'delete', url: '/yxt-admin/admin/hospital/' + maker_id[i] + '/delete', success: function(data) { if (data.stateCode == 200) { $.messager.show({ title: '提示消息', msg: data.message, timeout: 5000, showType: 'slide' }); }else if(data.stateCode==205){ $.messager.alert('提醒','登录已超时 重新登录','info',function(){ top.location.href="/yxt-admin/adminLogin.html"; }); } else { $.messager.show({ title: '提示', msg: data.message }); } }, error: function(XMLHttpRequest, textStatus, errorThrown) { $.messager.show({ title: '提示', msg: '系统错误,请联系开发人员.或刷新当前页面,重新操作。' }); } }); } location.reload(); }else{ map.removeOverlay(circle) } }) } var circleMenu=new BMap.ContextMenu(); circleMenu.addItem(new BMap.MenuItem('删除',removecircle.bind(removecircle))); // 圆上右键删除 circle.addContextMenu(circleMenu);// 添加右键菜单 } }, 继续完善就可以了!
在写项目的时候出现了一个这样的问题,虽然问题解决了,但是还是有点疑问。 在数据库中设计的表的一个字段为是否审核(is_vaild) 类型 tinyint(1) 对应的在 java中就是布尔类型(boolean) 1 已审核 true ; 0 未审核 false 。在一下的地方使用到了! 显示在页面上的是否审核。 修改是否审核: 在页面是已审核状态,后台数据取出显示为true。 之前设计: // 这是在没有修改是否审核的状态 出现的结果 这是三目运算符起作用了 (但是我没有修改是否审核的状态) 这时数据库修改值为0. 刚测试了几次 这样又可以了 不知道原因是什么!