用Mockito测试SpringMVC+Hibernate

译自:Spring 4 MVC+Hibernate 4+MySQL+Maven integration + Testing example using annotations

2017-01-19

目录:

1 目录结构
2 pom.xml
3 Testing Controller Layer
  3.1 com.websystique.springmvc.controller.AppControllerTest
4 Testing Service Layer
  4.1 com.websystique.springmvc.service.EmployeeServiceImplTest
5 Testing Data Layer
  5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration
  5.2 com.websystique.springmvc.dao.EntityDaoImplTest
  5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest
  5.4 src/test/resources/Employee.xml

源代码 : SpringHibernateExample.zip

1 目录结构


返回

2 pom.xml


返回

与 被测项目 Spring 4 MVC+Hibernate 4+MySQL+Maven使用注解集成实例中pom.xml 一样。

其中,

  • Spring-test : 在测试类中使用 spring-test annotations
  • TestNG : 使用testNG作为测试框架
  • Mockito : 使用mockito模拟外部依赖, 比如当测试service时mock dao,关于mockito,请参考Mockito教程
  • DBUnit : 使用DBUnit管理数据,当测试data/dao层时
  • H2 Database : 对数据库层测试,与其说是单元测试不如说是集成测试,使用H2 Database对数据库层进行测试

3 Testing Controller Layer


返回

3.1 com.websystique.springmvc.controller.AppControllerTest

package com.websystique.springmvc.controller;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.Mockito.atLeastOnce; import org.springframework.context.MessageSource;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee;
import com.websystique.springmvc.service.EmployeeService; public class AppControllerTest { @Mock
EmployeeService service; @Mock
MessageSource message; @InjectMocks
AppController appController; @Spy
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result; @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void listEmployees(){
when(service.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(appController.listEmployees(model), "allemployees");
Assert.assertEquals(model.get("employees"), employees);
verify(service, atLeastOnce()).findAllEmployees();
} @Test
public void newEmployee(){
Assert.assertEquals(appController.newEmployee(model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertFalse((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 0);
} @Test
public void saveEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "success");
Assert.assertEquals(model.get("success"), "Employee Axel registered successfully");
} @Test
public void editEmployee(){
Employee emp = employees.get(0);
when(service.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(appController.editEmployee(anyString(), model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertTrue((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 1);
} @Test
public void updateEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model, ""), "success");
Assert.assertEquals(model.get("success"), "Employee Axel updated successfully");
} @Test
public void deleteEmployee(){
doNothing().when(service).deleteEmployeeBySsn(anyString());
Assert.assertEquals(appController.deleteEmployee("123"), "redirect:/list");
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}

右击该测试类,得到结果如下:

PASSED: deleteEmployee
PASSED: editEmployee
PASSED: listEmployees
PASSED: newEmployee
PASSED: saveEmployeeWithSuccess
PASSED: saveEmployeeWithValidationError
PASSED: saveEmployeeWithValidationErrorNonUniqueSSN
PASSED: updateEmployeeWithSuccess
PASSED: updateEmployeeWithValidationError
PASSED: updateEmployeeWithValidationErrorNonUniqueSSN ===============================================
Default test
Tests run: 10, Failures: 0, Skips: 0
===============================================

解读:

因为被测类AppController依赖EmployeeService , MessageSource, Employee, ModelMap & BindingResult。因此,为了测试AppController,需要提供这些依赖。

    @Mock  //Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
EmployeeService service; @Mock
MessageSource message; @InjectMocks //InjectMocks创建这个类的对象并自动将标记@Mock、@Spy等注解的属性值注入到这个中
AppController appController; @Spy //Spy是一个真实的对象,但它可以设置对象行为
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result;

其中,when..then 模式用于设置对象行为。

另外,需要加入以下代码:

    MockitoAnnotations.initMocks(this); //初始化被注释的[@Mock, @Spy, @Captor, @InjectMocks] 对象

4 Testing Service Layer


返回

4.1 com.websystique.springmvc.service.EmployeeServiceImplTest

package com.websystique.springmvc.service;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import static org.mockito.Mockito.when; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.dao.EmployeeDao;
import com.websystique.springmvc.model.Employee; public class EmployeeServiceImplTest { @Mock
EmployeeDao dao; @InjectMocks
EmployeeServiceImpl employeeService; @Spy
List<Employee> employees = new ArrayList<Employee>(); @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void findById(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
Assert.assertEquals(employeeService.findById(emp.getId()),emp);
} @Test
public void saveEmployee(){
doNothing().when(dao).saveEmployee(any(Employee.class));
employeeService.saveEmployee(any(Employee.class));
verify(dao, atLeastOnce()).saveEmployee(any(Employee.class));
} @Test
public void updateEmployee(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
employeeService.updateEmployee(emp);
verify(dao, atLeastOnce()).findById(anyInt());
} @Test
public void deleteEmployeeBySsn(){
doNothing().when(dao).deleteEmployeeBySsn(anyString());
employeeService.deleteEmployeeBySsn(anyString());
verify(dao, atLeastOnce()).deleteEmployeeBySsn(anyString());
} @Test
public void findAllEmployees(){
when(dao.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(employeeService.findAllEmployees(), employees);
} @Test
public void findEmployeeBySsn(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.findEmployeeBySsn(anyString()), emp);
} @Test
public void isEmployeeSsnUnique(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.isEmployeeSsnUnique(emp.getId(), emp.getSsn()), true);
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}

右击该测试类,得到结果如下:

PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: isEmployeeSsnUnique
PASSED: saveEmployee
PASSED: updateEmployee ===============================================
Default test
Tests run: 7, Failures: 0, Skips: 0
===============================================

Test Service Layer和Test Control Layer类似,不再详述

5 Testing Data Layer


返回

DAO 或 data Layer测试一直是有争议的话题。我们到底要如何测试?把它当做单元测试的话,就要测试它的每一行代码,这样的话,要mocking所有的外部依赖。但是,我们没有与数据库本身的交互,就没法测data-layer。那么它就变成了集成测试。

通常,我们对DAO Layer做集成测试。这里,我们用in-memory H2 database做集成测试。

5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration

package com.websystique.springmvc.configuration;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; /*
* This class is same as real HibernateConfiguration class in sources.
* Only difference is that method dataSource & hibernateProperties
* implementations are specific to Hibernate working with H2 database.
*/ @Configuration
@EnableTransactionManagement
@ComponentScan({ "com.websystique.springmvc.dao" })
public class HibernateTestConfiguration { @Autowired
private Environment environment; @Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.websystique.springmvc.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
} @Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
} private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
} @Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}

解读:

  • 上面的类与HibernateConfiguration类非常相似,区别仅在 dataSource() & hibernateProperties()这两个方法的实现。
  • 在Sources folder中,它做了几乎同样事情:它用dataSource创建了SessionFacoty,其中,dataSource被配置成可与in-memory database H2一起工作。为了使hibernate与H2一起工作,设置hibernate.dialect为H2Dialect。
  • SessionFacoty会被注入到AbstractDao,而后当测试EmployeeDaoImpl类时,EmployeeDaoImpl会使用SessionFacoty。

5.2 com.websystique.springmvc.dao.EntityDaoImplTest

该类是所有测试累的基类

package com.websystique.springmvc.dao;

import javax.sql.DataSource;

import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.BeforeMethod; import com.websystique.springmvc.configuration.HibernateTestConfiguration; @ContextConfiguration(classes = { HibernateTestConfiguration.class })
public abstract class EntityDaoImplTest extends AbstractTransactionalTestNGSpringContextTests { @Autowired
DataSource dataSource; @BeforeMethod
public void setUp() throws Exception {
IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
dataSource);
DatabaseOperation.CLEAN_INSERT.execute(dbConn, getDataSet());
} protected abstract IDataSet getDataSet() throws Exception; }

解读:

  • AbstractTransactionalTestNGSpringContextTests在某种程度上可以更JUnit的RunWith等价。这个抽象类集成Spring TestContext support到TestNG environment中。
  • 为了在测试中提供数据访问层的支持,它也需要在ApplicationContext中定义datasource和transactionManager。我们已在上面的Configuration类中定义了datasource和transactionManager。
  • 由于事物支持,每次测试前一个事物会被默认启动,在每次测试结束后这个事物会被回滚。你可以override这个回滚行为。
  • BeforeTest在测试用执行前,我们将使用DBUnit去clean-insert测试数据库[h2]中的数据样例。这样避免各个测试方法之间的影响
  • 抽象方法getDataSet会在测试类中实现为了在测试前提供真实的测试数据

5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest

package com.websystique.springmvc.dao;

import java.math.BigDecimal;

import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee; public class EmployeeDaoImplTest extends EntityDaoImplTest{ @Autowired
EmployeeDao employeeDao; @Override
protected IDataSet getDataSet() throws Exception{
IDataSet dataSet = new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml"));
return dataSet;
} /* In case you need multiple datasets (mapping different tables) and you do prefer to keep them in separate XML's
@Override
protected IDataSet getDataSet() throws Exception {
IDataSet[] datasets = new IDataSet[] {
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Benefits.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Departements.xml"))
};
return new CompositeDataSet(datasets);
}
*/ @Test
public void findById(){
Assert.assertNotNull(employeeDao.findById(1));
Assert.assertNull(employeeDao.findById(3));
} @Test
public void saveEmployee(){
employeeDao.saveEmployee(getSampleEmployee());
Assert.assertEquals(employeeDao.findAllEmployees().size(), 3);
} @Test
public void deleteEmployeeBySsn(){
employeeDao.deleteEmployeeBySsn("11111");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 1);
} @Test
public void deleteEmployeeByInvalidSsn(){
employeeDao.deleteEmployeeBySsn("23423");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findAllEmployees(){
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findEmployeeBySsn(){
Assert.assertNotNull(employeeDao.findEmployeeBySsn("11111"));
Assert.assertNull(employeeDao.findEmployeeBySsn("14545"));
} public Employee getSampleEmployee(){
Employee employee = new Employee();
employee.setName("Karen");
employee.setSsn("12345");
employee.setSalary(new BigDecimal(10980));
employee.setJoiningDate(new LocalDate());
return employee;
} }

5.4 src/test/resources/Employee.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<employee id="1" NAME="SAMY" JOINING_DATE="2014-04-16" SALARY="20000" SSN="11111" />
<employee id="2" NAME="TOMY" JOINING_DATE="2014-05-17" SALARY="23000" SSN="11112" />
</dataset>

右击该测试类,得到结果如下:

PASSED: deleteEmployeeByInvalidSsn
PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: saveEmployee ===============================================
Default test
Tests run: 6, Failures: 0, Skips: 0
===============================================

我们以saveEmployee为例解读下执行过程:

1. 在测试方法运行前,Spring会通过@ContextConfiguration注释的EntityDaoImplTest类加载text context,还会通过AbstractTransactionalTestNGSpringContextTests创建beans实例。这只会发生一次。

2. 在bean实例创建前,Spring会创建SessionFactory Bean,并且SessionFactory Bean会被注入dataSource bean(在HibernateTestConfiguration类中定义),见下面属性设置

properties.put("hibernate.hbm2ddl.auto", "create-drop");  

注意:由于hbm2ddl属性,当SessionFactory被创建,与Model类相关的schema会被验证并导出到数据库。这意味着Employee表会在H2数据库中创建。

3. 在测试前,@BeforeMethod注释的方法会被调用,该方法会通知DBUnit连接数据库执行clean-insert,在Employee表插入两个记录(见Employee.xml内容)

4. 现在测试用例saveEmployee将开始执行,在执行开始前,事物将被启动,saveEmployee方法本身将在事物中运行。一旦saveEmployee方法运行完毕,事物会回滚到默认的setup。

5. 测试用例saveEmployee开始执行。它会调用employeeDao.saveEmployee(getSampleEmployee()),被调用者会通过hibernate插入预先定义的Employee到H2 database中。这是关键的一步。在这一个之后,就会有3条记录在H2 database中。

6. 在下一个用例中,@BeforeMethod又会被调用

7. 当所有用例测试完后,session会被关掉,schema会被去除

用Mockito测试SpringMVC+Hibernate的更多相关文章

  1. Maven搭建SpringMVC+Hibernate项目详解 【转】

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  2. Maven搭建SpringMVC+Hibernate项目详解

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  3. Maven搭建SpringMVC+Hibernate项目详解(转)

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  4. 框架篇:Spring+SpringMVC+hibernate整合开发

    前言: 最近闲的蛋疼,搭个框架写成博客记录下来,拉通一下之前所学知识,顺带装一下逼. 话不多说,我们直接步入正题. 准备工作: 1/ IntelliJIDEA的安装配置:jdk/tomcat等..(本 ...

  5. Spring+SpringMVC+Hibernate小案例(实现Spring对Hibernate的事务管理)

    原文地址:https://blog.csdn.net/jiegegeaa1/article/details/81975286 一.工作环境 编辑器用的是MyEclipse,用Mysql数据库,mave ...

  6. springmvc+hibernate

    <本文摘要他人> 1.设计数据库:设计好表结构,最好符合3NF,采用Hibernate tools将设计好的表自动生成对应的实体entity. 1.创建Maven项目,按需映入Maven包 ...

  7. spring+springmvc+hibernate 整合

    三大框架反反复复搭了很多次,虽然每次都能搭起来,但是效率不高.最近重新搭了一次,理顺了思路,整理了需要注意的地方,分享出来. 工具:Eclipse(jdk 1.7) spring和hibernate版 ...

  8. Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建

    Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...

  9. spring+springmvc+hibernate 礼品管理系统

    spring+springmvc+hibernate template礼品管理系统 1.简单介绍 如标题所示,这篇文章简单写了一个基于spring+springmvc+hibernate templa ...

随机推荐

  1. 强制修改mysql 中root的密码

    /etc/init.d/mysqld stop   (service mysqld stop )/usr/bin/mysqld_safe --skip-grant-tables另外开个SSH连接[ro ...

  2. Golang学习笔记:包制作

    golang的包跟java as js之类的大不一样,一定要存在GOPATH里面,GOPATH是专门用于存放golang第三方的库,里面有两个文件夹, src:源代码目录 pkg:编译后的第三方包,这 ...

  3. CodeForces 616B Dinner with Emma

    水题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; +; ...

  4. Struts2---声明式异常处理

    在service方法里 throw抛出一个异常, 然后再方法声明上加上throws: public List<Category> list() throws SQLException{ C ...

  5. MySQL 索引的使用

    一.or 的使用 (1)MySQL版本大于 5.x 的会使用 index merge 功能,即可以将多个单列索引集合起来使用,不过在查询时使用 or 的话,引擎为 myisam 的会开启 index ...

  6. Spark1.3.0安装

    之前在用Hadoop写ML算法的时候就隐约感觉Hadoop实在是不适合ML这些比较复杂的算法.记得当时写完kmeans后,发现每个job完成后都需要将结果放在HDFS中,然后下次迭代的时候再从文件中读 ...

  7. NSDateFormatter调整时间格式的代码

    NSDateFormatter调整时间格式的代码 在开发iOS程序时,有时候需要将时间格式调整成自己希望的格式,这个时候我们可以用NSDateFormatter类来处理.例如://实例化一个NSDat ...

  8. 苹果应用商店AppStore审核中文指南 分类: ios相关 app相关 2015-07-27 15:33 84人阅读 评论(0) 收藏

    目录 1. 条款与条件 2. 功能 3. 元数据.评级与排名 4. 位置 5. 推送通知 6. 游戏中心 7. 广告 8. 商标与商业外观 9. 媒体内容 10. 用户界面 11. 购买与货币 12. ...

  9. Mac iTerm2使用rz、sz从远程上传下载文件

    使用 brew install lrzsz .如果安装遇到错误的话,使用以下方法: 在mac终端下运行: brew install lrzsz (安装教程:http://brew.sh/index_z ...

  10. Nodejs之使用session

    nodejs中使用session的说明. session介绍 为什么使用session: session运行在服务器端,当客户端第一次访问服务器时,可以将客户的登陆信息保存. 当客户访问其他界面时,可 ...