JDBC
项目结构
.
├── build.gradle
└── src
└── main
├── java
│ └── com
│ └── yww
│ ├── config
│ │ └── DatabaseConfig.java
│ ├── JdbcController.java
│ ├── JdbcTemplateController.java
│ ├── sql
│ │ ├── schema.sql
│ │ └── test-data.sql
│ └── User.java
├── resources
│ └── application.properties
└── webapp
├── index.jsp
└── WEB-INF
├── applicationContext.xml
├── dispatcher-servlet.xml
├── jsp
│ └── show.jsp
├── sql
│ ├── schema.sql
│ └── test-data.sql
└── web.xml
示例数据库
示例用的数据库,表。(账号:root,密码:123456)
DROP DATABASE demo_db;
CREATE DATABASE if NOT exists demo_db;
use demo_db;
create table if NOT exists user_t(
id int auto_increment,
name VARCHAR(10),
passwd VARCHAR(20),
PRIMARY KEY (id)
) engine=InnoDB;
INSERT INTO user_t(id, name, passwd)
VALUES
(1001, "admin", "123456"),
(1002, "yww", "123456");
SELECT * from user_t;
传统方式
import java.sql.*;
public class Main {
public static void main(String[] args){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理获取数据库连接
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/demo_db",
"root",
"123456");
// 获取预处理
preparedStatement = connection.prepareStatement("select * from user_t where id = ?");
// 设置参数
preparedStatement.setString(1, "1001");
// 执行查询语句
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//释放资源
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Spring的数据访问哲学
异常体系
不同于JDBC,Spring提供了多个数据访问异常。
这些异常都继承自DataAccessException
,是非检查型异常(可以不捕获)。
数据访问模板
Spring将数据访问过程中固定的
和可变的
部分明确划分为2部分:模板(template)和回调(callback)。
模板 | 回调 |
---|---|
1.准备事务 2.开始事务 -> |
3.在事务中执行 |
5.提交/回滚事务 6.关闭资源和处理错误 |
<- 4.返回数据 |
Spring所提供的部分数据访问模板及用途:
模板类(org.springframework.*) | 用途 |
---|---|
jdbc.core.JdbcTemplate | JDBC连接 |
jdbc.core.namedparam.NamedParameterJdbcTemplate | 支持命名参数的JDBC连接 |
orm.hibernate3.HibernateTemplate | Hibernate 3.x以上的Session |
orm.jpa.JpaTemplate | Java持久化API的实体管理器 |
配置数据源
无论那种数据访问方式,都需要配置一个数据源引用。
Spring提供了在Spring上下文中配置数据源bean的多种方式:
- 通过
JDBC驱动程序定义
的数据源。 - 通过
JNDI查找
的数据源。 连接池
的数据源。
JNDI数据源
java方式:
[DatabaseConfig.java
]
package com.yww.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jndi.JndiObjectFactoryBean;
@Configuration
public class DatabaseConfig {
@Bean
public JndiObjectFactoryBean dataSource(){
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/MyDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return jndiObjectFactoryBean;
}
}
jndi-name
指定JNDI中资源的名称。如果应用程序运行在java应用服务器中,需要将resource-ref
设置为true,这样给定的jndi-name
将会自动添加"java:/comp/env/"
前缀。
xml方式:
[applicationContext.xml
]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/MyDS" resource-ref="true" />
</beans>
数据源连接池java方式:
需要在build.gradle
中添加apache.commons.dbcp
(这里使用的dbcp2)。
compile "org.apache.commons:commons-dbcp2:2.7.0"
java方式:
[DatabaseConfig.java
]
package com.yww.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DatabaseConfig {
@Bean
public BasicDataSource dataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/demo_db");
ds.setUsername("root");
ds.setPassword("123456");
ds.setInitialSize(5);
ds.setMaxTotal(10);
return ds;
}
}
xml方式:
[applicationContext.xml
]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/demo_db"
p:username="root"
p:password="123456"
p:initialSize="5"
p:maxTotal="10" />
</beans>
基于JDBC驱动的数据源
这是最简单的方式。
Spring提供了3个数据源类(位于org.springframework.jdbc.datasource):
DriverManagerDataSource
:每次连接请求时都会返回一个新建的连接。SimpleDriverDataSource
:与上一个类似,但直接使用JDBC驱动,来解决特定环境下类的加载问题。SingleConnectionDataSource
:每次请求都会返回同一个连接。
java方式:
[DatabaseConfig.java
]
package com.yww.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/demo_db");
// ds.setUrl("jdbc:mysql://localhost:3306/demo_db?characterEncoding=utf8&useSSL=false"); // xml中&转义为&
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
}
xml方式:
[applicationContext.xml
]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/demo_db"
p:username="root"
p:password="123456" />
</beans>
嵌入式数据源
需要在build.gradle
中添加h2
。
compile "com.h2database:h2:1.4.200"
java方式:
[DatabaseConfig.java
]
package com.yww.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import javax.sql.DataSource;
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript(("classpath:com/yww/sql/schema.sql"))
.addScript("classpath:com/yww/sql/test-data.sql")
.build();
}
}
xml方式:
[applicationContext.xml
]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<jdbc:embedded-database id="dataSource" type="H2">
<!-- <jdbc:script location="classpath:com/yww/sql/schema.sql" />-->
<jdbc:script location="./WEB-INF/sql/schema.sql" />
<jdbc:script location="file:WEB-INF/sql/test-data.sql" />
</jdbc:embedded-database>
</beans>
sql文件的路径,可使用类路径或文件路径。
profile选择数据源
需要在application.properties
文件中配置:
spring.profiles.default=dev
spring.profiles.active=production
package com.yww.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;
import javax.sql.DataSource;
@Configuration
public class DatabaseConfig {
@Profile("dev")
@Bean
public DataSource embeddedDataSource(){
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript(("classpath:com/yww/sql/schema.sql"))
.addScript("classpath:com/yww/sql/test-data.sql")
.build();
}
@Profile("production")
@Bean
public DataSource dataSource(){
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/MyDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource)jndiObjectFactoryBean;
}
}
JDBC
传统用法
[JdbcController.java
]
package com.yww;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Controller
@RequestMapping("/show")
public class JdbcController {
@RequestMapping(method = RequestMethod.GET)
public String showData(ModelMap model){
String msg = query(1001);
System.out.println(msg);
model.addAttribute("msg", msg);
return "show";
}
@Autowired
DataSource dataSource;
public String query(int id){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet resultSet = null;
String result = "";
try{
conn = dataSource.getConnection(); // 获取连接
String sql = "select * from user_t where id = ?";
stmt = conn.prepareStatement(sql); // 创建语句
stmt.setString(1, String.valueOf(id)); // 绑定参数
resultSet = stmt.executeQuery(); // 执行语句
while (resultSet.next())
result = resultSet.getInt("id") + resultSet.getString("name") + resultSet.getString("passwd");
}catch (SQLException e){ // 处理异常
}finally { // 清理资源
try{
if(resultSet != null)
resultSet.close();
if(stmt != null)
stmt.close();
if(conn != null)
conn.close();
}catch (SQLException e){ // 处理异常
}
}
return result;
}
}
JDBC模板
Spring为JDBC提供了三个模板类供选择:
JdbcTemplate
:最基本的Spring JDBC模板,这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询。NamedParameterJdbcTemplate
:可以在执行查询时,将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数。SimpleJdbcTemplate
:利用一些如自动装箱,范型以及可变参数列表来简化JDBC模板的使用。
首先需要配置一个jdbc模板的Bean。
[DatabaseConfig.java
]
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
JdbcOperations
是一个接口,定义了JdbcTemplate
所实现的操作。
[JdbcTemplateController.java
]
package com.yww;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;
@Controller
@RequestMapping("/show_t")
public class JdbcTemplateController {
@Autowired
private JdbcOperations jdbcOperations;
@RequestMapping(method = RequestMethod.GET)
public String showData(ModelMap model){
Random random = new Random();
User user = new User(random.nextInt(10000), "yww", "123456");
addUser(user);
User userFound = findOne(1001);
model.addAttribute("msg", userFound);
return "show";
}
// 添加
public void addUser(User user){
String sql = "insert into user_t (id, name, passwd) values (?, ?, ?)";
jdbcOperations.update(sql,
user.getId(),
user.getName(),
user.getPasswd());
}
// 查询
public User findOne(int id){
String sql = "select * from user_t where id = ?";
return jdbcOperations.queryForObject(sql,
new UserRowMapper(), // 指定映射
// (rs, rowNum) -> {
// return new User(
// rs.getInt("id"),
// rs.getString("name"),
// rs.getString("passwd")
// );
// }, // 指定映射(java 8 lambda,函数式编程)
// this::mapUser, // 指定映射(Java 8 方法引用)
id);
}
// 映射类
public static final class UserRowMapper implements RowMapper<User>{
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return new User(
rs.getInt("id"),
rs.getString("name"),
rs.getString("passwd")
);
}
}
// 映射方法
public User mapUser(ResultSet rs, int row) throws SQLException{
return new User(
rs.getInt("id"),
rs.getString("name"),
rs.getString("passwd")
);
}
}
因为
RowMapper
接口只声明了addRow()
一个方法,因此它完全符合函数式接口
的标准,可以使用Lambda
表达式。或使用java 8中的方法引用,在单独的方法中定义映射逻辑。(可以不必显式实现RowMapper
接口,只需Lambda表达式或方法接受相同参数,返回相同类型)
简单描述一下
NamedParameterJdbcTemplate
的用法,先声明一个它的Bean,使用时,sql语句不用?
,和放入的参数为字典Map
类型。如下:public void addUserByName(User user){ String sql = "insert into user_t (id, name, passwd) values (:id, :name, :passwd)"; Map<String, Object> paramMap = new HashMap<>(); paramMap.put("id", user.getId()); paramMap.put("name", user.getName()); paramMap.put("passwd", user.getPasswd()); jdbcOperations.update(sql, paramMap); }
ORM
使用mybatis,hibernate之类的方式。
JPA
Spring Data JPA通过方法签名,自动创建方法的实现。(本质上Spring Data定义了领域特定语言(DSL))
由 动词 + 主题 + 关键词 + 断言 组成。
readSpittersByFirstnameOrLastnameOrderByLastname()
|__|_______|__|_________________________________|
查询动词 关键词
主题(可不指定) 断言
配置:需配置
LocalEntityManagerFactoryBean
或LocalContainerEntityManagerFactoryBean
的Bean,再配置JpaVendorAdapter
的Bean,再配置@EnableJpaRepositories
,最后自定义的Repository接口继承JpaRepository
。