spring访问数据库
JAVA使用JDBC
创建全局DataSource实例,表示数据库连接池;
在需要读写数据库的方法内部,按如下步骤访问数据库:
从全局DataSource实例获取Connection实例;
通过Connection实例创建PreparedStatement实例;
执行SQL语句,如果是查询,则通过ResultSet读取结果集,
如果是修改,则获得int结果。
Spring使用JDBC
第一步先引入maven:
引入jdbc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
开启注解
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
数据库连接池
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
示例用的数据库
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.5.0</version>
</dependency>
第二步
@Configuration
@ComponentScan
@PropertySource("jdbc.properties")
public class AppConfig {
@Value("${jdbc.url}")
String jdbcUrl;
@Value("${jdbc.username}")
String jdbcUsername;
@Value("${jdbc.password}")
String jdbcPassword;
@Bean
DataSource createDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(jdbcUrl);
config.setUsername(jdbcUsername);
config.setPassword(jdbcPassword);
config.addDataSourceProperty("autoCommit", "true");
config.addDataSourceProperty("connectionTimeout", "5");
config.addDataSourceProperty("idleTimeout", "60");
return new HikariDataSource(config);
}
//使用spring提供的JbdcTemplate来创建数据
@Bean
JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
第三步使用JdbcTemplate
有这几种使用方式
1. execute(ConnectionCallback<T> action) 这就是jdbc的connection的用法 都能用
jdbcTemplate.execute((Connection conn) -> {
// 可以直接使用conn实例,不要释放它,回调结束后JdbcTemplate自动释放:
// 在内部手动创建的PreparedStatement、ResultSet必须用try(...)释放:
try (var ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
ps.setObject(1, id);
try (var rs = ps.executeQuery()) {
if (rs.next()) {
return new User( // new User object:
rs.getLong("id"), // id
rs.getString("email"), // email
rs.getString("password"), // password
rs.getString("name")); // name
}
throw new RuntimeException("user not found by id.");
}
}
2. execute(String sql, PreparedStatementCallback<T> action)
jdbcTemplate.execute("SELECT * FROM users WHERE name = ?", (PreparedStatement ps) -> {
// PreparedStatement实例已经由JdbcTemplate创建,并在回调后自动释放:
ps.setObject(1, name);
try (var rs = ps.executeQuery()) {
if (rs.next()) {
...
}
throw new RuntimeException("user not found by id.");
}
});
3. queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)
jdbcTemplate.queryForObject("SELECT * FROM users WHERE email = ?", new Object[] { xxx@qq.com },
(ResultSet rs, int rowNum) -> {
// 将ResultSet的当前行映射为一个JavaBean:
return new User( // new User object:
rs.getLong("id"), // id
rs.getString("email"), // xxx@qq.com
rs.getString("password"), // password
rs.getString("name")); // name
});
jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", null, (ResultSet rs, int rowNum) -> {
// SELECT COUNT(*)查询只有一列,取第一列数据:
return rs.getLong(1);
});
4. query返回多条数据
jdbcTemplate.query("SELECT * FROM users LIMIT ? OFFSET ?", new Object[] { limit, offset },
new BeanPropertyRowMapper<>(User.class))
那么BeanPropertyRowMapper就可以直接把一行记录按列名转换为JavaBean 会自动映射
5. insert获取自增长的id 跟jdbc那的很相似 多了KeyHolder
public User register(String email, String password, String name) {
// 创建一个KeyHolder:
KeyHolder holder = new GeneratedKeyHolder();
if (1 != jdbcTemplate.update(
// 参数1:PreparedStatementCreator
(conn) -> {
// 创建PreparedStatement时,必须指定RETURN_GENERATED_KEYS:
var ps = conn.prepareStatement("INSERT INTO users(email,password,name) VALUES(?,?,?)",
Statement.RETURN_GENERATED_KEYS);
ps.setObject(1, email);
ps.setObject(2, password);
ps.setObject(3, name);
return ps;
},
// 参数2:KeyHolder
holder)
) {
throw new RuntimeException("Insert failed.");
}
// 从KeyHolder中获取返回的自增值:
return new User(holder.getKey().longValue(), email, password, name);
}
使用声明式事物
JDBC的事物是这样的
Connection conn = openConnection();
try {
// 关闭自动提交:
conn.setAutoCommit(false);
// 执行多条SQL语句:
insert(); update(); delete();
// 提交事务:
conn.commit();
} catch (SQLException e) {
// 回滚事务:
conn.rollback();
} finally {
conn.setAutoCommit(true);
conn.close();
}
那spring 的事物该如何使用呢?
Spring提供了一个PlatformTransactionManager来表示事务管理器,所有的事务都由它负责管理
事务由TransactionStatus表示
TransactionStatus tx = null;
try {
// 开启事务:
tx = txManager.getTransaction(new DefaultTransactionDefinition());
// 相关JDBC操作:
jdbcTemplate.update("...");
jdbcTemplate.update("...");
// 提交事务:
txManager.commit(tx);
} catch (RuntimeException e) {
// 回滚事务:
txManager.rollback(tx);
throw e;
}
为什么要抽象出PlatformTransactionManager 和 TransactionStatus ?
原因是JavaEE除了提供JDBC事务外,它还支持分布式事务JTA(Java Transaction API)。
分布式事务是指多个数据源(比如多个数据库,多个消息系统)要在分布式环境下实现事务的时候,
应该怎么实现。分布式事务实现起来非常复杂,简单地说就是通过一个分布式事务管理器实现两阶段提交,
但本身数据库事务就不快,基于数据库事务实现的分布式事务就慢得难以忍受,所以使用率不高
Spring为了同时支持JDBC和JTA两种事务模型,
就抽象出PlatformTransactionManager。因为我们的代码只需要JDBC事务
@Configuration
@ComponentScan
@PropertySource("jdbc.properties")
public class AppConfig {
...
@Bean
PlatformTransactionManager createTxManager(@Autowired DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
使用编程的方式使用Spring事务仍然比较繁琐,更好的方式是通过声明式事务来实现。使用声明式事务非常简单,
除了在AppConfig中追加一个上述定义的PlatformTransactionManager外,
再加一个@EnableTransactionManagement就可以启用声明式事务:
@Configuration
@ComponentScan
@EnableTransactionManagement // 启用声明式
@PropertySource("jdbc.properties")
public class AppConfig {
...
}
然后,对需要事务支持的方法,加一个@Transactional注解:
@Component
public class UserService {
// 此public方法自动具有事务支持:
@Transactional
public User register(String email, String password, String name) {
...
}
}
或者更简单一点,直接在Bean的class处加上,表示所有public方法都具有事务支持:
实现原理:
public class UserService$$EnhancerBySpringCGLIB extends UserService {
UserService target = ...
PlatformTransactionManager txManager = ...
public User register(String email, String password, String name) {
TransactionStatus tx = null;
try {
tx = txManager.getTransaction(new DefaultTransactionDefinition());
target.register(email, password, name);
txManager.commit(tx);
} catch (RuntimeException e) {
txManager.rollback(tx);
throw e;
}
}
...
}
回滚事务 定义好程序里的异常很利于事物回滚的控制
默认情况下,如果发生了RuntimeException,Spring的声明式事务将自动回滚。在一个事务方法中,
如果程序判断需要回滚事务,只需抛出RuntimeException,例如:
@Transactional
public buyProducts(long productId, int num) {
...
if (store < num) {
// 库存不够,购买失败:
throw new IllegalArgumentException("No enough products");
}
...
}
如果要针对Checked Exception回滚事务,需要在@Transactional注解中写出来:
@Transactional(rollbackFor = {RuntimeException.class, IOException.class})
public buyProducts(long productId, int num) throws IOException {
...
}
上述代码表示在抛出RuntimeException或IOException时,事务将回滚。
为了简化代码,我们强烈建议业务异常体系从RuntimeException派生,这样就不必声明任何特殊异常即可让Spring的声明式事务正常工作:
public class BusinessException extends RuntimeException {
...
}
public class LoginException extends BusinessException {
...
}
public class PaymentException extends BusinessException {
...
}
事务边界 和 事物传播
例子分析
@Component
public class BonusService {
@Transactional
public void addBonus(long userId, int bonus) { // 事务开始
...
} // 事务结束
}
@Component
public class UserService {
@Autowired
BonusService bonusService;
@Transactional
public User register(String email, String password, String name) {
// 插入用户记录:
User user = jdbcTemplate.insert("...");
// 增加100积分:
这个时候事物的边界是什么
bonusService.addBonus(user.id, 100);
}
}
以上结论是会先判断当前方法有没有事物,如果有事物就加入到当前的事物,默认的事物级别是REQUIRED
事物级别说明
SUPPORTS:表示如果有事务,就加入到当前事务,
如果没有,那也不开启事务执行。这种传播级别可用于查询方法,因为SELECT语句既可以在事务内执行,也可以不需要事务;
MANDATORY:表示必须要存在当前事务并加入执行,否则将抛出异常。
这种传播级别可用于核心更新逻辑,比如用户余额变更,它总是被其他事务方法调用,不能直接由非事务方法调用;
REQUIRES_NEW:表示不管当前有没有事务,都必须开启一个新的事务执行。
如果当前已经有事务,那么当前事务会挂起,等新事务完成后,再恢复执行;
NOT_SUPPORTED:表示不支持事务,如果当前有事务,那么当前事务会挂起,等这个方法执行完成后,再恢复执行;
NEVER:和NOT_SUPPORTED相比,它不但不支持事务,而且在监测到当前有事务时,会抛出异常拒绝执行;
NESTED:表示如果当前有事务,则开启一个嵌套级别事务,如果当前没有事务,则开启一个新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Product createProduct() {
...
}
事务能正确传播的前提是,方法调用是在一个线程内才行
使用DAO
通常是Web层调用业务层,业务层调用数据访问层
例如:
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
User getById(long id) {
...
}
List<User> getUsers(int page) {
...
}
User createUser(User user) {
...
}
User updateUser(User user) {
...
}
void deleteUser(User user) {
...
}
}
Spring提供了一个JdbcDaoSupport类,用于简化DAO的实现
public abstract class JdbcDaoSupport extends DaoSupport {
private JdbcTemplate jdbcTemplate;
public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
initTemplateConfig();
}
public final JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
...
}
它的意图是子类直接从JdbcDaoSupport继承后,可以随时调用getJdbcTemplate()获得JdbcTemplate的实例
那么问题来了:因为JdbcDaoSupport的jdbcTemplate字段没有标记@Autowired,所以,子类想要注入JdbcTemplate,还得自己想个办法:
@Component
@Transactional
public class UserDao extends JdbcDaoSupport {
@Autowired
JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
super.setJdbcTemplate(jdbcTemplate);
}
}
public abstract class AbstractDao extends JdbcDaoSupport {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
super.setJdbcTemplate(jdbcTemplate);
}
}
Hibernate
是一个比较成熟的ORM框架
public class AppConfig {
@Bean
LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource) {
var sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
// 扫描指定的package获取所有entity class:
sessionFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
var props = new Properties();
props.setProperty("hibernate.hbm2ddl.auto", "update"); // 生产环境不要使用
props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
props.setProperty("hibernate.show_sql", "true");
sessionFactoryBean.setHibernateProperties(props);
return sessionFactoryBean;
}
}
var sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
注意我们在定制Bean中讲到过FactoryBean,LocalSessionFactoryBean是一个FactoryBean,
它会再自动创建一个SessionFactory,在Hibernate中,Session是封装了一个JDBC Connection的实例,
而SessionFactory是封装了JDBC DataSource的实例,即SessionFactory持有连接池,每次需要操作数据库的时候,
SessionFactory创建一个新的Session,相当于从连接池获取到一个新的Connection。
SessionFactory就是Hibernate提供的最核心的一个对象,
但LocalSessionFactoryBean是Spring提供的为了让我们方便创建SessionFactory的类。
紧接着,我们还需要创建HibernateTemplate以及HibernateTransactionManager:
public class AppConfig {
@Bean
HibernateTemplate createHibernateTemplate(@Autowired SessionFactory sessionFactory) {
return new HibernateTemplate(sessionFactory);
}
@Bean
PlatformTransactionManager createTxManager(@Autowired SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
这两个Bean的创建都十分简单。HibernateTransactionManager是配合Hibernate使用声明式事务所必须的,
而HibernateTemplate则是Spring为了便于我们使用Hibernate提供的工具类,不是非用不可,但推荐使用以简化代码。
映射类的创建
@Entity
@Table(name="users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, updatable = false)
public Long getId() { ... }
@Column(nullable = false, unique = true, length = 100)
public String getEmail() { ... }
@Column(nullable = false, length = 100)
public String getPassword() { ... }
@Column(nullable = false, length = 100)
public String getName() { ... }
@Column(nullable = false, updatable = false)
public Long getCreatedAt() { ... }
}
主键id定义的类型不是long,而是Long。
这是因为Hibernate如果检测到主键为null,
就不会在INSERT语句中指定主键的值,而是返回由数据库生成的自增值,
否则,Hibernate认为我们的程序指定了主键的值,会在INSERT语句中直接列出。long型字段总是具有默认值0,
因此,每次插入的主键值总是0,导致除第一次外后续插入都将失败。
hibenate的增删改查
@Autowired
HibernateTemplate hibernateTemplate;
hibernateTemplate.save(user);
hibernateTemplate.delete(user);
hibernateTemplate.update(user);
User user = hibernateTemplate.get(User.class, id);
hibernateTemplate.update(user);
load()和get()都可以根据主键加载记录,它们的区别在于,当记录不存在时,get()返回null,而load()抛出异常。
查询有两种方式:
1. 使用example
User example = new User();
example.setEmail(email);
example.setPassword(password);
List<User> list = hibernateTemplate.findByExample(example);
2. 使用criteria
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
criteria.add(Restrictions.eq("email", email))
.add(Restrictions.eq("password", password));
List<User> list = (List<User>) hibernateTemplate.findByCriteria(criteria);
criteria的高级用法
SELECT * FROM user WHERE (email = ? OR name = ?) AND password = ?
criteria.add(
Restrictions.and(
Restrictions.or(
Restrictions.eq("email", email),
Restrictions.eq("name", email)
),
Restrictions.eq("password", password)
)
);
3. hql
List<User> list = (List<User>) hibernateTemplate.find("FROM User WHERE email=? AND password=?", email, password);
和SQL相比,HQL使用类名和属性名,由Hibernate自动转换为实际的表名和列名
除了可以直接传入HQL字符串外,Hibernate还可以使用一种NamedQuery,它给查询起个名字,然后保存在注解中
@NamedQueries(
@NamedQuery(
// 查询名称:
name = "login",
// 查询语句:
query = "SELECT u FROM User u WHERE u.email=?0 AND u.password=?1"
)
)
public User login(String email, String password) {
List<User> list = (List<User>) hibernateTemplate.findByNamedQuery("login", email, password);
return list.isEmpty() ? null : list.get(0);
}
直接写HQL和使用NamedQuery各有优劣。前者可以在代码中直观地看到查询语句,后者可以在User类统一管理所有相关查询。
4. 使用Hibernate原生接口
使用Hibernate的原生接口实际上总是从SessionFactory出发,它通常用全局变量存储,在HibernateTemplate中以成员变量被注入。
void operation() {
Session session = null;
boolean isNew = false;
// 获取当前Session或者打开新的Session:
try {
session = this.sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = this.sessionFactory.openSession();
isNew = true;
}
// 操作Session:
try {
User user = session.load(User.class, 123L);
}
finally {
// 关闭新打开的Session:
if (isNew) {
session.close();
}
}
}
集成JPA Java Persistence API
JPA从本质上讲就是java组织定义的一些orm的标准接口。hibernate就是jpa这个接口的具体实现
跟JDBC和mysql的关系是一样的
在Spring中集成JPA要选择一个实现,可以选择Hibernate或EclipseLink;
使用JPA与Hibernate类似,但注入的核心资源是带有@PersistenceContext注解的EntityManager代理类
用法基本和hibernate类似
1. 加入依赖 和 hiberate一样
org.springframework:spring-context:5.2.0.RELEASE
org.springframework:spring-orm:5.2.0.RELEASE
javax.annotation:javax.annotation-api:1.3.2
org.hibernate:hibernate-core:5.4.2.Final
com.zaxxer:HikariCP:3.4.2
org.hsqldb:hsqldb:2.5.0
2. 使用Hibernate时,我们需要创建一个LocalSessionFactoryBean,并让它再自动创建一个SessionFactory。
使用JPA也是类似的,我们需要创建一个LocalContainerEntityManagerFactoryBean,
并让它再自动创建一个EntityManagerFactory:
@Bean
LocalContainerEntityManagerFactoryBean createEntityManagerFactory(@Autowired DataSource dataSource) {
var entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
// 设置DataSource:
entityManagerFactoryBean.setDataSource(dataSource);
// 扫描指定的package获取所有entity class:
entityManagerFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
// 指定JPA的提供商是Hibernate:
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
// 设定特定提供商自己的配置:
var props = new Properties();
props.setProperty("hibernate.hbm2ddl.auto", "update");
props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
props.setProperty("hibernate.show_sql", "true");
entityManagerFactoryBean.setJpaProperties(props);
return entityManagerFactoryBean;
}
3. 还需要实例化一个JpaTransactionManager,以实现声明式事务:
@Bean
PlatformTransactionManager createTxManager(@Autowired EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
4. 增删改查
注入
我们需要注入一个EntityManager,但是不要使用Autowired,而是@PersistenceContext:
@Component
@Transactional
public class UserService {
@PersistenceContext
EntityManager em; 这其实是一个代理
}
Spring遇到标注了@PersistenceContext的EntityManager会自动注入代理,该代理会在必要的时候自动打开EntityManager。
换句话说,多线程引用的EntityManager虽然是同一个代理类,但该代理类内部针对不同线程会创建不同的EntityManager实例。
简单总结一下,标注了@PersistenceContext的EntityManager可以被多线程安全地共享。
get
public User getUserById(long id) {
User user = this.em.find(User.class, id);
if (user == null) {
throw new RuntimeException("User not found by id: " + id);
}
return user;
}
Criteria
// CriteriaBuilder:
var cb = em.getCriteriaBuilder();
CriteriaQuery<User> q = cb.createQuery(User.class);
Root<User> r = q.from(User.class);
q.where(cb.equal(r.get("email"), cb.parameter(String.class, "e")));
TypedQuery<User> query = em.createQuery(q);
// 绑定参数:
query.setParameter("e", email);
// 执行查询:
List<User> list = query.getResultList();
return list.isEmpty() ? null : list.get(0);
JPQL
TypedQuery<User> query = em.createQuery("SELECT u FROM User u WHERE u.email = :e", User.class);
query.setParameter("e", email);
List<User> list = query.getResultList();
if (list.isEmpty()) {
throw new RuntimeException("User not found by email.");
}
return list.get(0);
NamedQuery
@NamedQueries(
@NamedQuery(
name = "login",
query = "SELECT u FROM User u WHERE u.email=:e AND u.password=:p"
)
)
TypedQuery<User> query = em.createNamedQuery("login", User.class);
query.setParameter("e", email);
query.setParameter("p", password);
List<User> list = query.getResultList();
return list.isEmpty() ? null : list.get(0);
Mybatis
JPA 或者 Hiberate的作用是什么?
就是把结果的ResultSet的每一行变成java bean
或者把Java Bean自动转换到INSERT或UPDATE语句的参数中,从而实现ORM。
这是一个全自动的orm 没茨获取和设置都是动态创建的代理对象进行的判断.
但是这样是有缺点的.
全自动的orm的缺点:
1. 为了实现这样的查询,UserProxy必须保存Hibernate的当前Session。
但是,当事务提交后,Session自动关闭,此时再获取getAddress()将无法访问数据库,
或者获取的不是事务一致的数据。因此,
ORM框架总是引入了Attached/Detached状态,
表示当前此Java Bean到底是在Session的范围内,
还是脱离了Session变成了一个“游离”对象。
很多初学者无法正确理解状态变化和事务边界,就会造成大量的PersistentObjectException异常。
这种隐式状态使得普通Java Bean的生命周期变得复杂。
2.
此外,Hibernate和JPA为了实现兼容多种数据库,
它使用HQL或JPQL查询,
经过一道转换,变成特定数据库的SQL,
理论上这样可以做到无缝切换数据库,
但这一层自动转换除了少许的性能开销外,
给SQL级别的优化带来了麻烦
3.
ORM框架通常提供了缓存,并且还分为一级缓存和二级缓存。
一级缓存是指在一个Session范围内的缓存,常见的情景是根据主键查询时,两次查询可以返回同一实例:
User user1 = session.load(User.class, 123);
User user2 = session.load(User.class, 123);
二级缓存是指跨Session的缓存,一般默认关闭,需要手动配置。
二级缓存极大的增加了数据的不一致性,原因在于SQL非常灵活,常常会导致意外的更新
对比Spring提供的JdbcTemplate,它和ORM框架相比,主要有几点差别:
查询后需要手动提供Mapper实例以便把ResultSet的每一行变为Java对象;
增删改操作所需的参数列表,需要手动传入,即把User实例变为[user.id, user.name, user.email]这样的列表,比较麻烦。
但是JdbcTemplate的优势在于它的确定性:即每次读取操作一定是数据库操作而不是缓存,
所执行的SQL是完全确定的,缺点就是代码比较繁琐,构造INSERT INTO users VALUES (?,?,?)更是复杂。
为了取其精华去其糟粕? 于是诞生了半自动化的数据库框架mybatis
1. 使用
spring官方没有集成 需要自己集成
引入
org.mybatis:mybatis:3.5.4
org.mybatis:mybatis-spring:2.0.4
2. 创建datadruid
@Bean
DataSource createDataSource() { ... }
3.
可见,ORM的设计套路都是类似的。使用MyBatis的核心就是创建SqlSessionFactory,这里我们需要创建的是SqlSessionFactoryBean:
@Bean
SqlSessionFactoryBean createSqlSessionFactoryBean(@Autowired DataSource dataSource) {
var sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
4.
@Bean
PlatformTransactionManager createTxManager(@Autowired DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
和Hibernate不同的是,MyBatis使用Mapper来实现映射,而且Mapper必须是接口。
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User getById(@Param("id") long id);
}
注意:这里的Mapper不是JdbcTemplate的RowMapper的概念,
它是定义访问users表的接口方法。比如我们定义了一个User getById(long)的主键查询方法,
不仅要定义接口方法本身,还要明确写出查询的SQL,
这里用注解@Select标记。SQL语句的任何参数
,都与方法参数按名称对应。
MyBatis是一个半自动化的ORM框架,需要手写SQL语句,没有自动加载一对多或多对一关系的功能