在做数据存储方面,最早是通过JDBC访问数据库,在这个阶段大部分的程序员需要手写SQL语句,解析数据并映射到内存中的模型。后来人们发现天天写这些重复又容易出错的代码不是办法,于是诞生了两个神器 - iBatis 和 Hibernate .
iBatis (即myBatis) 可以自动把数据库查询结果映射到内存模型,但还是需要开发者自己写SQL语句 - 对于iBatis来说这是一种优势通过手写SQL可以提高SQL的效率。Hibernate更牛逼, 直接把内存中的模型映射到数据库的表,隐藏了SQL的存在,开发者只要定义好数据模型,配置一下数据库就可以操作数据库了。这两种框架一般叫做 ORM 框架 - Object Relational Mapping.
在ORM时代,主流的数据库是关系型数据库,在社交网络兴起之后NoSQL也逐渐流行起来,这让数据访问变得更加复杂。SpringData提供了一套方案希望屏蔽底层的复杂性,使用SpringData仅仅需要定义接口即可,它在底层会通过动态代理实现对各种数据库的兼容。
Spring Data – One API To Rule Them All?SpringDataJPA
JPA是Java的一套ORM框架标准,SpringDataJPA通过这套标准实现了对关系型数据库的支持。通过定义接口可减少很多重复的Repository代码的编写,加快开发速度。
下面会编写一个应用,通过SpringDataJPA把一个内存对象 Customer
写入/读出内存数据库H2.
- 工程结构,一个Gradle构建文件,3个Java文件
➜ spring-data-jpa tree .
.
├── build.gradle
└── src
└── main
└── java
└── hello
├── App.java
├── Customer.java
└── CustomerRepository.java
```
2. Gradle文件 `build.gradle`
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.7.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'spring-boot'
repositories {
mavenCentral()
maven { url "https://repository.jboss.org/nexus/content/repositories/releases" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("com.h2database:h2")
testCompile("junit:junit")
}
```
- 模型
Customer.java
,包含三个属性:id
、firstName
、lastName
, 注意这里全是JPA标准注解。
package hello;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
protected Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format("Customer[id=%d, firstName='%s', lastName='%s']",id, firstName, lastName);
}
}
```
4. 定义数据访问Repo - `CustomerRepository.java`,这就是SpringDataJPA的牛逼之处仅仅需要定义接口即可。
package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
```
- 在
App.java
写一些测试代码
package hello;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a couple of customers
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
// fetch customers by last name
log.info("Customer found with findByLastName('Bauer'):");
log.info("--------------------------------------------");
for (Customer bauer : repository.findByLastName("Bauer")) {
log.info(bauer.toString());
}
log.info("");
};
}
}
```
6. 运行测试,因为有`main()`的缘故,旨在在`App.java`中执行`main()`方法即可。打印就略过了。
看到这里肯定是一头雾水:
1. 没有配置数据库?
2. 没有集成ORM或者写SQL语句?
第一个问题,在 `build.gradle`文件中有一个依赖 `compile("com.h2database:h2")` 这是一个内存数据库,SpringData会自动配置这个数据库。
第二个问题,就是SpringData的神奇之处了,既没有写任何SQL语句也没有集成ORM框架,但是却可以把数据写入h2数据库再读出来。SpringData通过动态代理实现了 `CustomerRepository` 接口,并且约定了 `findByLastName` 这种格式(`LastName`是定义在`Customer` 中的一个属性),它会被解析成一个SQL语句 - 按照数据库列`lastName`进行查找。如果不走这种约定也可以通过`@Query`注解来绑定SQL语句,具体再参照文档。
* [官方文档](http://docs.spring.io/spring-data/data-jpa/docs/1.4.x/reference/htmlsingle/#repositories.custom-implementations)
*