- 浏览: 446987 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (153)
- linux (17)
- struts2 (3)
- mongodb (1)
- mysql (13)
- ant (1)
- java (12)
- hibernate,spring (4)
- 测试 (3)
- eclipse (2)
- svn (2)
- hudson (2)
- 程序员 (1)
- maven (3)
- jenkins (1)
- vnc (1)
- sublime (1)
- shell (6)
- cookie,session,token区别 (2)
- idea (4)
- git (6)
- hive (3)
- 效率 (2)
- nginx (2)
- vim (1)
- 高性能mysql (1)
- js (1)
- http (1)
- 消息队列 (1)
- 单点登录 (1)
- ElementUI (1)
- springboot (1)
- redis (1)
- CommandLineRunner (1)
- 存储过程 (1)
- 慢查询 (1)
- mysql主从 (1)
- canal (1)
- jdk proxy (1)
- cglib (1)
- JVM (8)
- 字节码 (1)
- 分布式 (2)
- rpc (1)
- 网卡 (2)
- IM (1)
- QA (1)
最新评论
-
sunshine_bean:
值得参考
异常 A web application registered the JBDC driver -
babyhhcsy:
你跟进出来结果没有啊
To prevent a memory leak, the JDBC Driver has been forcibly unregistered -
左岸代码右岸诗:
换tomcat的版本不是解决问题的根本方法啊,继续寻找解决方案 ...
异常 A web application registered the JBDC driver -
ywf008:
我在win7环境下报java.net.UnknownHostE ...
java.net.UnknownHostException 异常处理 -
javapub:
用目前oracle提供的java7,上面说的新特性好像都编译不 ...
Java 7七大新功能预览z
根据SpringSide4中涉及到的ORM框架,针对具体实践进行学习,做出比较和总结,以便在实际系统架构设计中能够更好地选择。
Spring Data JPA
概述
Spring Data :提供了一整套数据访问层(DAO)的解决方案,致力于减少数据访问层(DAO)的开发量。它使用一个叫作Repository的接口类为基础,它被定义为访问底层数据模型的超级接口。而对于某种具体的数据访问操作,则在其子接口中定义。
所有继承这个接口的interface都被spring所管理,此接口作为标识接口,功能就是用来控制domain模型的。
Spring Data可以让我们只定义接口,只要遵循spring data的规范,就无需写实现类。
示例
public interface UserDao extends Repository<AccountInfo, Long> {
public AccountInfo save(AccountInfo accountInfo);
}
使用方法
使用步驟
声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,当然如果有需要,Spring Data 也提供了若干 Repository 子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
在接口中声明需要的业务方法。Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。
在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
接口类型
Spring Data JPA提供了四种不同的接口来满足不同的需求
Repository 接口
默认只实现在接口中声明的方法,所以每一个接口都需要声明相似的增删改查方法,适用于严格控制查询方法的情况。
CrudRepository 接口
能够默认的实现增删改查的持久化方法,适用于常规的持久化方法实现。
PagingAndSortingRepository 接口
继承自 CrudRepository 接口,在 CrudRepository 基础上新增了两个与分页有关的方法。更灵活的方式是继承 Repository 或 CrudRepository 的基础上,在自己声明的方法参数列表最后增加一个 Pageable 或 Sort 类型的参数。
JpaRepository 接口
继承自 PagingAndSortingRepository 的针对 JPA 技术提供的接口,并提供了其他一些方法,比如 flush(),saveAndFlush(),deleteInBatch() 等。
三种创建查询的方法
通过解析方法名创建查询
在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):
先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。
同时也考虑到了重复属性的情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上"_"以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
NotNull --- 与 IsNotNull 等价;
Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);
NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
使用 @Query 创建查询
@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL(HQL) 查询语句即可,如下所示:
// 使用 @Query 提供自定义查询语句示例
public interface UserDao extends Repository<AccountInfo, Long> {
@Query("select a from AccountInfo a where a.accountId = ?1")
public AccountInfo findByAccountId(Long accountId);
@Query("select a from AccountInfo a where a.balance > ?1")
public Page<AccountInfo> findByBalanceGreaterThan(
Integer balance,Pageable pageable);
}
很多开发者在创建 JP QL 时喜欢使用命名参数来代替位置编号,@Query 也对此提供了支持。JP QL 语句中通过": 变量"的格式来指定参数,同时在方法的参数前面使用 @Param 将方法参数与 JP QL 中的命名参数对应,示例如下:
public interface UserDao extends Repository<AccountInfo, Long> {
public AccountInfo save(AccountInfo accountInfo);
@Query("from AccountInfo a where a.accountId = :id")
public AccountInfo findByAccountId(@Param("id")Long accountId);
@Query("from AccountInfo a where a.balance > :balance")
public Page<AccountInfo> findByBalanceGreaterThan(
@Param("balance")Integer balance,Pageable pageable);
}
此外,开发者也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。如下所示:
@Modifying
@Query("update AccountInfo a set a.salary = ?1 where a.salary < ?2")
public int increaseSalary(int after, int before);
通过调用 JPA 命名查询语句创建查询
public interface UserDao extends Repository<AccountInfo, Long> {
......
public List<AccountInfo> findTop5();
}
如果希望为 findTop5() 创建命名查询,并与之关联,我们只需要在适当的位置定义命名查询语句,并将其命名为 "AccountInfo.findTop5",框架在创建代理类的过程中,解析到该方法时,优先查找名为 "AccountInfo.findTop5" 的命名查询定义,如果没有找到,则尝试解析方法名,根据方法名字创建查询。
@NamedQuery(name="AccountInfo.findTop5",query="SELECT u FROM User u"),
Spring Data JPA 对事务的支持
默认情况下,Spring Data JPA 实现的方法都是使用事务的。针对查询类型的方法,其等价于 @Transactional(readOnly=true);增删改类型的方法,等价于 @Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。
也可以在接口方法上使用 @Transactional 显式指定事务属性,该值覆盖 Spring Data JPA 提供的默认值。
为接口中的部分方法提供自定义实现
有些时候,开发者可能需要在某些方法中做一些特殊的处理,此时自动生成的代理对象不能完全满足要求。为了享受 Spring Data JPA 带给我们的便利,同时又能够为部分方法提供自定义实现,我们可以采用如下的方法:
将需要开发者手动实现的方法从持久层接口(假设为 AccountDao )中抽取出来,独立成一个新的接口(假设为 AccountDaoPlus ),并让 AccountDao 继承 AccountDaoPlus;
为 AccountDaoPlus 提供自定义实现(假设为 AccountDaoPlusImpl );
将 AccountDaoPlusImpl 配置为 Spring Bean;
在 <jpa:repositories> 中按如下方式进行配置。
<jpa:repositories base-package="footmark.springdata.jpa.dao">
<jpa:repository id="accountDao" repository-impl-ref=" accountDaoPlus " />
</jpa:repositories>
<bean id="accountDaoPlus" class="......."/>
<jpa:repositories > 提供了一个 repository-impl-postfix 属性,用以指定实现类的后缀。
<jpa:repositories base-package="footmark.springdata.jpa.dao"
repository-impl-postfix="Impl"/>
则在框架扫描到 AccountDao 接口时,它将尝试在相同的包目录下查找 AccountDaoImpl.java,如果找到,便将其中的实现方法作为最终生成的代理类中相应方法的实现。
总结
Spring Data JPA 能够和Spring无缝集成,但不依赖Spring,能够作为更好的ORM框架来使用,通过用持久层继承不同的接口,Spring Data JPA能够帮助开发人员免去为持久层接口编写实现类的工作,同时多样话的接口、查询方法名解析与灵活的注解,都能够满足简单到复杂业务的持久化需求。
使用方案
与Maven集成的配置
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.4.3.RELEASE</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
</exclusion>
</exclusions>
</dependency>
在Spring配置文件中的配置
下载Spring Data JPA 的发布包(需要同时下载 Spring Data Commons 和 Spring Data JPA 两个发布包,Commons 是 Spring Data 的公共基础包),并把相关的依赖 JAR 文件加入到 CLASSPATH 中。
并在Spring配置文件中添加如下配置
<-- 需要在 <beans> 标签中增加对 jpa 命名空间的引用 -->
<jpa:repositories base-package="footmark.springdata.jpa.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
JPA/Hibernate
概述
Hibernate提供了一套JPA标准的实现,拥有强大的持久化方法,简化开发人员在持久层的编码工作,同时也提供了完善的缓存的机制,帮助开发者更好地进行复杂业务逻辑的持久化操作。
用法
Hibernate/JPA不但实现了Java的注解接口,也加入了一些扩展的注解。
Hibernate通过三个组件来实现JPA:
hibernate-annotation:是Hibernate支持annotation方式配置的基础,它包括了标准的JPA annotation以及Hibernate自身特殊功能的annotation。
hibernate-entitymanager:是Hibernate的核心实现,提供了Hibernate所有的核心功能
hibernate-core:实现了标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。
实体的状态
实体共有4种状态:
新建态:新创建的实体对象,尚未拥有持久化主键,没有和一个持久化上下文关联起来
受控态:已经拥有持久化主键和持久化上下文建立了联系
游离态:拥有持久化主键,但尚未和持久化上下文建立联系
删除态:拥有持久化主键,已经和持久化上下文建立了联系,但已经被安排从数据库中删除
EntityManager的API
void persist(Object entity)
通过persist方法,新实体实例将转换为受控状态,就是说,当persist()方法所在的事务提交时,实体的数据保存到数据库中。
如果实体已经被持久化,那么调用persist()方法不会发生任何事情。
如果对一个已经删除的实体调用persist()方法,删除态的实体又转变为受控态
如果对游离状态的实体执行persist()操作,抛出IllegalArgumentException
一个实体调用persist()方法后,所有与之关联的实体,都将执行持久化操作
void remove(Object entity)
删除一个受控态的实体。
如果实体声明为级联删除(cascade=REMOVE或者cascade=ALL),被关联的实体也会被删除
在一个新建态或删除态的实体上调用remove()方法,将被忽略
在游离态的实体上调用remove()方法,将抛出IllegalArgumentException,相关事务将回滚
void flush()
将受控态的实体数据同步到数据库中
T merge(T entity)
将一个游离态的实体持久化到数据库中,并转换为受控态的实体
T find(Class entityClass.Object primaryKey)
以主键查询实体对象,entityClass是实体的类,primaryKey是主键值
Eg:Topic t = em.find(Topic.class,1);
使用步骤
1.在classpath根部META-INF目录下创建persistence.xml文件,内容如下:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="course">
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.connection.driver_class" value="org.gjt.mm.mysql.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/db_jpa"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
2.创建实体类,并加上JPA注解(也可以使用XML形式的注解),代码如下:
@Entity
@Table(name="t_course")
public class Course {
@Id
@GeneratedValue
private Long id;
private String title;
private Date beginDate;
private Date endDate;
private int fee;
//省略get、set方法
}
3.编写Dao接口,代码如下:
public interface CourseDao {
public void save(Course course);
public void delete(Long id);
public Course get(Long id);
}
4.编写Dao接口的JPA实现类,代码如下:
public class JpaCourseDao implements CourseDao {
//实体对象由实体管理器进行管理,通过EntityManager和持久化上下文进行交互
private EntityManagerFactory entityManagerFactory;
public JpaCourseDao() {
//根据persistence.xml中定义的PersistenceUnit的命名来创建实体管理器
entityManagerFactory = Persistence.createEntityManagerFactory("course");
}
@Override
public void delete(Long id) {
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
Course course = manager.find(Course.class, id);
manager.remove(course);
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
manager.close();
}
}
@Override
public Course get(Long id) {
EntityManager manager = entityManagerFactory.createEntityManager();
try {
return manager.find(Course.class, id);
} finally {
manager.close();
}
}
@Override
public void save(Course course) {
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
manager.merge(course);
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
manager.close();
}
}
}
与Spring集成方案
1.在Spring的applicationContext.xml中引入如下配置
<!-- entityManagerFactory 有以下2种方式提供 -->
<!-- class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">-->
<!-- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Test_PU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
2.在src的META-INF目录下建立persistence.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="Test_PU" transaction-type="RESOURCE_LOCAL">
<!-- provider>org.eclipse.persistence.jpa.PersistenceProvider</provider-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- MYSql 的连接-->
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/tester?useUnicode=true&characterEncoding=utf-8" />
<property name="hibernate.connection.username" value="tester" />
<property name="hibernate.connection.password" value="tester" />
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.max_size" value="100"/>
<property name="hibernate.c3p0.min_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="120"/>
<property name="hibernate.c3p0.max_statements" value="0"/>
<property name="hibernate.c3p0.idle_test_period" value="120"/>
<property name="hibernate.c3p0.acquire_increment" value="5 "/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.cache.use_query_cache" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.useUnicode" value="true"/>
<property name="hibernate.characterEncoding" value="utf8"/>
</properties>
</persistence-unit>
</persistence>
3.建立TestaService.java,实现了findbyID 和add方法以及一个JPQL的用法。
//此处定义为全部的服务方法都由事物控制
@Transactional
@Service("com.alcor.test.service.TestaService")
public class TestaService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestaService.class);
@Autowired
private EchoService echoService;
@PersistenceContext
EntityManager em;
//覆盖默认的事物配置
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public Testa findByID(TestaPK id) {
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - start");
}
logger.debug(id.getId()+"|"+id.getName());
Testa returnTesta = em.find(Testa.class, id);
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - end");
}
return returnTesta;
}
//使用了默认的事物机制
public void add(){
if (logger.isDebugEnabled()) {
logger.debug("add() - start");
}
//保存A表
Testa testa = new Testa();
TestaPK testaPK = new TestaPK();
testaPK.setId(UUID.randomUUID().toString());
testaPK.setName(UUID.randomUUID().toString());
testa.setId(testaPK);
em.persist(testa);
//保存B表
Testb testb = new Testb();
testb.setId(UUID.randomUUID().toString());
em.persist(testb);
//调用一个autowired 的service
echoService.doNothing();
if (logger.isDebugEnabled()) {
logger.debug("add() - end");
}
}
/**
* 通过使用JPQL 来做查询
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public List<Testa> findAllBySex (){
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - start");
}
String queryString = "SELECT a FROM Testa a WHERE a.age < :age AND a.id.name like :name";
Query query = em.createQuery(queryString);
query.setParameter("name", "%xv%");
query.setParameter("age", 20);
List<Testa> results = query.getResultList();
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - end");
}
return results;
}
}
总结
Hibernate/JPA在用法上根据不同的集成方式而改变,但其核心的功能都是一样的,其重点是实现了JPA的标准ORM规范,使持久层的操作与具体的框架解耦。
它在Hibernate里写法上是session,而在JPA中变成了manager,所以从Hibernate到JPA的代价应该是非常小的。
MyBatis
概述
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java
对象)映射成数据库中的记录。
用法
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
使用步骤
MyBatis在不同的框架环境下有不同的配置方式,但具体的步骤如下:
配置sqlSessionFactory实例
sqlSessionFactory为MyBatis框架的核心实体,我们需要在项目中配置sqlSessionFactory极其属性,包括数据源,实体映射配置,查询语句的配置等信息。详情查看MyBatis配置文件详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/hlp?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" />
<property name="username" value="root" />
<property name="password" value="1234" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/config/UserDaoMapper.xml"/>
</mappers>
</configuration>
创建实体类
package com.mybatis.config;
public class User {
private int userId;
private String userName;
private String password;
private String comment;
//省略get/set方法
}
创建数据层接口(Dao)
只需要创建接口,不用担心实现类
package com.mybatis.config;
import java.util.List;
public interface UserDao {
public int insert(User user);
public int update(User user);
public int delete(String userName);
public List<User> selectAll();
public int countAll();
public User findByUserName(String userName);
}
配置查询映射文件
将与实体相关的查询语句和数据层接口对应配置,详细参照MyBatis中Mapper文件详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.config.UserDao">
<select id="countAll" resultType="int">
select count(*) c from user;
</select>
<select id="selectAll" resultType="com.mybatis.config.User">
select * from user order by userName asc
</select>
<insert id="insert" parameterType="com.mybatis.config.User">
insert into user(userName,password,comment) values(#{userName},#{password},#{comment})
</insert>
<update id="update" parameterType="com.mybatis.config.User">
update user set userName=#{userName},password=#{password},comment=#{comment} where userName=#{userName}
</update>
<delete id="delete" parameterType="int">
delete from user where userName=#{userName}
</delete>
<select id="findByUserName" parameterType="String" resultType="com.mybatis.config.User">
select * from user where userName=#{userName}
</select>
</mapper>
测试查询
package com.mybatis.config;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
public class UserDaoTest {
@Test
public void userDaoTest() throws Exception
{
String resource = "MyBatis-Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(reader);
SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
User user = new User();
user.setUserName("hongye");
user.setPassword("123456");
user.setComment("备注");
userDao.insert(user);
System.out.println("记录条数:"+userDao.countAll());
List<User> users = userDao.selectAll();
Iterator<User> iter = users.iterator();
while(iter.hasNext()){
User u = iter.next();
System.out.println("用户名:"+u.getUserName()+"密码:"+u.getPassword());
}
user.setComment("comment");
userDao.update(user);
User u = userDao.findByUserName("hongye");
System.out.println(u.getComment());
userDao.delete("hongye");
System.out.println("记录条数:"+userDao.countAll());
session.commit();
session.close();
}
}
与Spring整合方案
根据MyBatis的使用步骤,按照SpringSide4中的最佳实践,给出与Spring整合的方案
引入MyBatis依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
配置核心sqlSessionFactory实体,在applicationContext.xml中如下配置
<!-- MyBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="typeAliasesPackage" value="org.springside.examples.showcase.entity" />
<!-- 显式指定Mapper文件位置 -->
<property name="mapperLocations" value="classpath:/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描basePackage下所有以@MyBatisRepository标识的 接口-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.springside.examples.showcase" />
<property name="annotationClass" value="org.springside.examples.showcase.repository.mybatis.MyBatisRepository"/>
</bean>
实体类(略过)
数据层接口
//通过@MapperScannerConfigurer扫描目录中的所有接口,动态在Spring Context中生成实现.
@MyBatisRepository
public interface UserMybatisDao {
User get(Long id);
List<User> search(Map<String, Object> parameters);
void save(User user);
void delete(Long id);
}
Mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace必须指向Dao接口 -->
<mapper namespace="org.springside.examples.showcase.repository.mybatis.UserMybatisDao">
<!--
获取用户: 输出直接映射到对象, login_name列要"as loginName"以方便映射 ,team_id as "team.id"创建team对象并赋值
-->
<select id="get" parameterType="int" resultType="User">
select id, name, email,
login_name as loginName,
team_id as "team.id"
from ss_user
where id=#{id}
</select>
<!-- 查询用户,演示: 1.输入用map传入多个参数 2.<where>语句, 智能添加where和and关键字 3.输出直接映射对象 -->
<select id="search" parameterType="map" resultType="User">
select id, name, email,
login_name as loginName,
team_id as "team.id"
from ss_user
<where>
<if test="loginName != null">
login_name=#{loginName}
</if>
<if test="name != null">
and name=#{name}
</if>
</where>
</select>
<!-- 插入用户: 1.由数据库生成id并赋值到user对象 2.输入用对象, 嵌套属性表达式#{team.id} -->
<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into ss_user (
login_name, name, password, email, team_id)
values (
#{loginName}, #{name}, #{password}, #{email},
#{team.id})
</insert>
<!-- 删除用户 -->
<delete id="delete" parameterType="int">
delete from ss_user where id=#{id}
</delete>
</mapper>
測試
@DirtiesContext
@ContextConfiguration(locations = { "/applicationContext.xml" })
public class UserMybatisDaoTest extends SpringTransactionalTestCase {
@Autowired
private UserMybatisDao userDao;
@Test
public void getUser() throws Exception {
User user = userDao.get(1L);
assertNotNull("User not found", user);
assertEquals("admin", user.getLoginName());
}
@Test
public void searchUser() throws Exception {
Map<String, Object> parameter = Maps.newHashMap();
parameter.put("name", "管理员");
List<User> result = userDao.search(parameter);
assertEquals(1, result.size());
assertEquals("admin", result.get(0).getLoginName());
}
@Test
public void createAndDeleteUser() throws Exception {
// create
int count = countRowsInTable("ss_user");
User user = UserData.randomUser();
userDao.save(user);
Long id = user.getId();
assertEquals(count + 1, countRowsInTable("ss_user"));
User result = userDao.get(id);
assertEquals(user.getLoginName(), result.getLoginName());
// delete
userDao.delete(id);
assertEquals(count, countRowsInTable("ss_user"));
assertNull(userDao.get(id));
}
}
总结
iBATIS 的着力点,则在于POJO与SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。相对Hibernate“O/R”而言,iBATIS 是一种“Sql Mapping”的ORM实现。
MyBatis的学习难度相对较低,但需要手写大量的sql语句,且不如Hibernate封装性强,适合使用在对于没有那么高的对象模型要求的项目。
具体与Hibernate的比较参考Hibernate与 MyBatis的比较
作者:Caos
链接:https://www.jianshu.com/p/61724b0989e1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Spring Data JPA
概述
Spring Data :提供了一整套数据访问层(DAO)的解决方案,致力于减少数据访问层(DAO)的开发量。它使用一个叫作Repository的接口类为基础,它被定义为访问底层数据模型的超级接口。而对于某种具体的数据访问操作,则在其子接口中定义。
所有继承这个接口的interface都被spring所管理,此接口作为标识接口,功能就是用来控制domain模型的。
Spring Data可以让我们只定义接口,只要遵循spring data的规范,就无需写实现类。
示例
public interface UserDao extends Repository<AccountInfo, Long> {
public AccountInfo save(AccountInfo accountInfo);
}
使用方法
使用步驟
声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,当然如果有需要,Spring Data 也提供了若干 Repository 子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
在接口中声明需要的业务方法。Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。
在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
接口类型
Spring Data JPA提供了四种不同的接口来满足不同的需求
Repository 接口
默认只实现在接口中声明的方法,所以每一个接口都需要声明相似的增删改查方法,适用于严格控制查询方法的情况。
CrudRepository 接口
能够默认的实现增删改查的持久化方法,适用于常规的持久化方法实现。
PagingAndSortingRepository 接口
继承自 CrudRepository 接口,在 CrudRepository 基础上新增了两个与分页有关的方法。更灵活的方式是继承 Repository 或 CrudRepository 的基础上,在自己声明的方法参数列表最后增加一个 Pageable 或 Sort 类型的参数。
JpaRepository 接口
继承自 PagingAndSortingRepository 的针对 JPA 技术提供的接口,并提供了其他一些方法,比如 flush(),saveAndFlush(),deleteInBatch() 等。
三种创建查询的方法
通过解析方法名创建查询
在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):
先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。
同时也考虑到了重复属性的情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上"_"以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
NotNull --- 与 IsNotNull 等价;
Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);
NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
使用 @Query 创建查询
@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL(HQL) 查询语句即可,如下所示:
// 使用 @Query 提供自定义查询语句示例
public interface UserDao extends Repository<AccountInfo, Long> {
@Query("select a from AccountInfo a where a.accountId = ?1")
public AccountInfo findByAccountId(Long accountId);
@Query("select a from AccountInfo a where a.balance > ?1")
public Page<AccountInfo> findByBalanceGreaterThan(
Integer balance,Pageable pageable);
}
很多开发者在创建 JP QL 时喜欢使用命名参数来代替位置编号,@Query 也对此提供了支持。JP QL 语句中通过": 变量"的格式来指定参数,同时在方法的参数前面使用 @Param 将方法参数与 JP QL 中的命名参数对应,示例如下:
public interface UserDao extends Repository<AccountInfo, Long> {
public AccountInfo save(AccountInfo accountInfo);
@Query("from AccountInfo a where a.accountId = :id")
public AccountInfo findByAccountId(@Param("id")Long accountId);
@Query("from AccountInfo a where a.balance > :balance")
public Page<AccountInfo> findByBalanceGreaterThan(
@Param("balance")Integer balance,Pageable pageable);
}
此外,开发者也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。如下所示:
@Modifying
@Query("update AccountInfo a set a.salary = ?1 where a.salary < ?2")
public int increaseSalary(int after, int before);
通过调用 JPA 命名查询语句创建查询
public interface UserDao extends Repository<AccountInfo, Long> {
......
public List<AccountInfo> findTop5();
}
如果希望为 findTop5() 创建命名查询,并与之关联,我们只需要在适当的位置定义命名查询语句,并将其命名为 "AccountInfo.findTop5",框架在创建代理类的过程中,解析到该方法时,优先查找名为 "AccountInfo.findTop5" 的命名查询定义,如果没有找到,则尝试解析方法名,根据方法名字创建查询。
@NamedQuery(name="AccountInfo.findTop5",query="SELECT u FROM User u"),
Spring Data JPA 对事务的支持
默认情况下,Spring Data JPA 实现的方法都是使用事务的。针对查询类型的方法,其等价于 @Transactional(readOnly=true);增删改类型的方法,等价于 @Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。
也可以在接口方法上使用 @Transactional 显式指定事务属性,该值覆盖 Spring Data JPA 提供的默认值。
为接口中的部分方法提供自定义实现
有些时候,开发者可能需要在某些方法中做一些特殊的处理,此时自动生成的代理对象不能完全满足要求。为了享受 Spring Data JPA 带给我们的便利,同时又能够为部分方法提供自定义实现,我们可以采用如下的方法:
将需要开发者手动实现的方法从持久层接口(假设为 AccountDao )中抽取出来,独立成一个新的接口(假设为 AccountDaoPlus ),并让 AccountDao 继承 AccountDaoPlus;
为 AccountDaoPlus 提供自定义实现(假设为 AccountDaoPlusImpl );
将 AccountDaoPlusImpl 配置为 Spring Bean;
在 <jpa:repositories> 中按如下方式进行配置。
<jpa:repositories base-package="footmark.springdata.jpa.dao">
<jpa:repository id="accountDao" repository-impl-ref=" accountDaoPlus " />
</jpa:repositories>
<bean id="accountDaoPlus" class="......."/>
<jpa:repositories > 提供了一个 repository-impl-postfix 属性,用以指定实现类的后缀。
<jpa:repositories base-package="footmark.springdata.jpa.dao"
repository-impl-postfix="Impl"/>
则在框架扫描到 AccountDao 接口时,它将尝试在相同的包目录下查找 AccountDaoImpl.java,如果找到,便将其中的实现方法作为最终生成的代理类中相应方法的实现。
总结
Spring Data JPA 能够和Spring无缝集成,但不依赖Spring,能够作为更好的ORM框架来使用,通过用持久层继承不同的接口,Spring Data JPA能够帮助开发人员免去为持久层接口编写实现类的工作,同时多样话的接口、查询方法名解析与灵活的注解,都能够满足简单到复杂业务的持久化需求。
使用方案
与Maven集成的配置
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.4.3.RELEASE</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
</exclusion>
</exclusions>
</dependency>
在Spring配置文件中的配置
下载Spring Data JPA 的发布包(需要同时下载 Spring Data Commons 和 Spring Data JPA 两个发布包,Commons 是 Spring Data 的公共基础包),并把相关的依赖 JAR 文件加入到 CLASSPATH 中。
并在Spring配置文件中添加如下配置
<-- 需要在 <beans> 标签中增加对 jpa 命名空间的引用 -->
<jpa:repositories base-package="footmark.springdata.jpa.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
JPA/Hibernate
概述
Hibernate提供了一套JPA标准的实现,拥有强大的持久化方法,简化开发人员在持久层的编码工作,同时也提供了完善的缓存的机制,帮助开发者更好地进行复杂业务逻辑的持久化操作。
用法
Hibernate/JPA不但实现了Java的注解接口,也加入了一些扩展的注解。
Hibernate通过三个组件来实现JPA:
hibernate-annotation:是Hibernate支持annotation方式配置的基础,它包括了标准的JPA annotation以及Hibernate自身特殊功能的annotation。
hibernate-entitymanager:是Hibernate的核心实现,提供了Hibernate所有的核心功能
hibernate-core:实现了标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。
实体的状态
实体共有4种状态:
新建态:新创建的实体对象,尚未拥有持久化主键,没有和一个持久化上下文关联起来
受控态:已经拥有持久化主键和持久化上下文建立了联系
游离态:拥有持久化主键,但尚未和持久化上下文建立联系
删除态:拥有持久化主键,已经和持久化上下文建立了联系,但已经被安排从数据库中删除
EntityManager的API
void persist(Object entity)
通过persist方法,新实体实例将转换为受控状态,就是说,当persist()方法所在的事务提交时,实体的数据保存到数据库中。
如果实体已经被持久化,那么调用persist()方法不会发生任何事情。
如果对一个已经删除的实体调用persist()方法,删除态的实体又转变为受控态
如果对游离状态的实体执行persist()操作,抛出IllegalArgumentException
一个实体调用persist()方法后,所有与之关联的实体,都将执行持久化操作
void remove(Object entity)
删除一个受控态的实体。
如果实体声明为级联删除(cascade=REMOVE或者cascade=ALL),被关联的实体也会被删除
在一个新建态或删除态的实体上调用remove()方法,将被忽略
在游离态的实体上调用remove()方法,将抛出IllegalArgumentException,相关事务将回滚
void flush()
将受控态的实体数据同步到数据库中
T merge(T entity)
将一个游离态的实体持久化到数据库中,并转换为受控态的实体
T find(Class entityClass.Object primaryKey)
以主键查询实体对象,entityClass是实体的类,primaryKey是主键值
Eg:Topic t = em.find(Topic.class,1);
使用步骤
1.在classpath根部META-INF目录下创建persistence.xml文件,内容如下:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="course">
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.connection.driver_class" value="org.gjt.mm.mysql.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/db_jpa"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
2.创建实体类,并加上JPA注解(也可以使用XML形式的注解),代码如下:
@Entity
@Table(name="t_course")
public class Course {
@Id
@GeneratedValue
private Long id;
private String title;
private Date beginDate;
private Date endDate;
private int fee;
//省略get、set方法
}
3.编写Dao接口,代码如下:
public interface CourseDao {
public void save(Course course);
public void delete(Long id);
public Course get(Long id);
}
4.编写Dao接口的JPA实现类,代码如下:
public class JpaCourseDao implements CourseDao {
//实体对象由实体管理器进行管理,通过EntityManager和持久化上下文进行交互
private EntityManagerFactory entityManagerFactory;
public JpaCourseDao() {
//根据persistence.xml中定义的PersistenceUnit的命名来创建实体管理器
entityManagerFactory = Persistence.createEntityManagerFactory("course");
}
@Override
public void delete(Long id) {
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
Course course = manager.find(Course.class, id);
manager.remove(course);
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
manager.close();
}
}
@Override
public Course get(Long id) {
EntityManager manager = entityManagerFactory.createEntityManager();
try {
return manager.find(Course.class, id);
} finally {
manager.close();
}
}
@Override
public void save(Course course) {
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction tx = manager.getTransaction();
try {
tx.begin();
manager.merge(course);
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
} finally {
manager.close();
}
}
}
与Spring集成方案
1.在Spring的applicationContext.xml中引入如下配置
<!-- entityManagerFactory 有以下2种方式提供 -->
<!-- class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">-->
<!-- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="Test_PU" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
2.在src的META-INF目录下建立persistence.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="Test_PU" transaction-type="RESOURCE_LOCAL">
<!-- provider>org.eclipse.persistence.jpa.PersistenceProvider</provider-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- MYSql 的连接-->
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/tester?useUnicode=true&characterEncoding=utf-8" />
<property name="hibernate.connection.username" value="tester" />
<property name="hibernate.connection.password" value="tester" />
<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
<property name="hibernate.c3p0.max_size" value="100"/>
<property name="hibernate.c3p0.min_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="120"/>
<property name="hibernate.c3p0.max_statements" value="0"/>
<property name="hibernate.c3p0.idle_test_period" value="120"/>
<property name="hibernate.c3p0.acquire_increment" value="5 "/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<property name="hibernate.cache.use_query_cache" value="false"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.useUnicode" value="true"/>
<property name="hibernate.characterEncoding" value="utf8"/>
</properties>
</persistence-unit>
</persistence>
3.建立TestaService.java,实现了findbyID 和add方法以及一个JPQL的用法。
//此处定义为全部的服务方法都由事物控制
@Transactional
@Service("com.alcor.test.service.TestaService")
public class TestaService {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(TestaService.class);
@Autowired
private EchoService echoService;
@PersistenceContext
EntityManager em;
//覆盖默认的事物配置
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public Testa findByID(TestaPK id) {
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - start");
}
logger.debug(id.getId()+"|"+id.getName());
Testa returnTesta = em.find(Testa.class, id);
if (logger.isDebugEnabled()) {
logger.debug("findByID(TestaPK) - end");
}
return returnTesta;
}
//使用了默认的事物机制
public void add(){
if (logger.isDebugEnabled()) {
logger.debug("add() - start");
}
//保存A表
Testa testa = new Testa();
TestaPK testaPK = new TestaPK();
testaPK.setId(UUID.randomUUID().toString());
testaPK.setName(UUID.randomUUID().toString());
testa.setId(testaPK);
em.persist(testa);
//保存B表
Testb testb = new Testb();
testb.setId(UUID.randomUUID().toString());
em.persist(testb);
//调用一个autowired 的service
echoService.doNothing();
if (logger.isDebugEnabled()) {
logger.debug("add() - end");
}
}
/**
* 通过使用JPQL 来做查询
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
public List<Testa> findAllBySex (){
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - start");
}
String queryString = "SELECT a FROM Testa a WHERE a.age < :age AND a.id.name like :name";
Query query = em.createQuery(queryString);
query.setParameter("name", "%xv%");
query.setParameter("age", 20);
List<Testa> results = query.getResultList();
if (logger.isDebugEnabled()) {
logger.debug("findAllBySex() - end");
}
return results;
}
}
总结
Hibernate/JPA在用法上根据不同的集成方式而改变,但其核心的功能都是一样的,其重点是实现了JPA的标准ORM规范,使持久层的操作与具体的框架解耦。
它在Hibernate里写法上是session,而在JPA中变成了manager,所以从Hibernate到JPA的代价应该是非常小的。
MyBatis
概述
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java
对象)映射成数据库中的记录。
用法
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
使用步骤
MyBatis在不同的框架环境下有不同的配置方式,但具体的步骤如下:
配置sqlSessionFactory实例
sqlSessionFactory为MyBatis框架的核心实体,我们需要在项目中配置sqlSessionFactory极其属性,包括数据源,实体映射配置,查询语句的配置等信息。详情查看MyBatis配置文件详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/hlp?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" />
<property name="username" value="root" />
<property name="password" value="1234" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/config/UserDaoMapper.xml"/>
</mappers>
</configuration>
创建实体类
package com.mybatis.config;
public class User {
private int userId;
private String userName;
private String password;
private String comment;
//省略get/set方法
}
创建数据层接口(Dao)
只需要创建接口,不用担心实现类
package com.mybatis.config;
import java.util.List;
public interface UserDao {
public int insert(User user);
public int update(User user);
public int delete(String userName);
public List<User> selectAll();
public int countAll();
public User findByUserName(String userName);
}
配置查询映射文件
将与实体相关的查询语句和数据层接口对应配置,详细参照MyBatis中Mapper文件详解
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.config.UserDao">
<select id="countAll" resultType="int">
select count(*) c from user;
</select>
<select id="selectAll" resultType="com.mybatis.config.User">
select * from user order by userName asc
</select>
<insert id="insert" parameterType="com.mybatis.config.User">
insert into user(userName,password,comment) values(#{userName},#{password},#{comment})
</insert>
<update id="update" parameterType="com.mybatis.config.User">
update user set userName=#{userName},password=#{password},comment=#{comment} where userName=#{userName}
</update>
<delete id="delete" parameterType="int">
delete from user where userName=#{userName}
</delete>
<select id="findByUserName" parameterType="String" resultType="com.mybatis.config.User">
select * from user where userName=#{userName}
</select>
</mapper>
测试查询
package com.mybatis.config;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
public class UserDaoTest {
@Test
public void userDaoTest() throws Exception
{
String resource = "MyBatis-Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(reader);
SqlSession session = factory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
User user = new User();
user.setUserName("hongye");
user.setPassword("123456");
user.setComment("备注");
userDao.insert(user);
System.out.println("记录条数:"+userDao.countAll());
List<User> users = userDao.selectAll();
Iterator<User> iter = users.iterator();
while(iter.hasNext()){
User u = iter.next();
System.out.println("用户名:"+u.getUserName()+"密码:"+u.getPassword());
}
user.setComment("comment");
userDao.update(user);
User u = userDao.findByUserName("hongye");
System.out.println(u.getComment());
userDao.delete("hongye");
System.out.println("记录条数:"+userDao.countAll());
session.commit();
session.close();
}
}
与Spring整合方案
根据MyBatis的使用步骤,按照SpringSide4中的最佳实践,给出与Spring整合的方案
引入MyBatis依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
配置核心sqlSessionFactory实体,在applicationContext.xml中如下配置
<!-- MyBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="typeAliasesPackage" value="org.springside.examples.showcase.entity" />
<!-- 显式指定Mapper文件位置 -->
<property name="mapperLocations" value="classpath:/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描basePackage下所有以@MyBatisRepository标识的 接口-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.springside.examples.showcase" />
<property name="annotationClass" value="org.springside.examples.showcase.repository.mybatis.MyBatisRepository"/>
</bean>
实体类(略过)
数据层接口
//通过@MapperScannerConfigurer扫描目录中的所有接口,动态在Spring Context中生成实现.
@MyBatisRepository
public interface UserMybatisDao {
User get(Long id);
List<User> search(Map<String, Object> parameters);
void save(User user);
void delete(Long id);
}
Mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace必须指向Dao接口 -->
<mapper namespace="org.springside.examples.showcase.repository.mybatis.UserMybatisDao">
<!--
获取用户: 输出直接映射到对象, login_name列要"as loginName"以方便映射 ,team_id as "team.id"创建team对象并赋值
-->
<select id="get" parameterType="int" resultType="User">
select id, name, email,
login_name as loginName,
team_id as "team.id"
from ss_user
where id=#{id}
</select>
<!-- 查询用户,演示: 1.输入用map传入多个参数 2.<where>语句, 智能添加where和and关键字 3.输出直接映射对象 -->
<select id="search" parameterType="map" resultType="User">
select id, name, email,
login_name as loginName,
team_id as "team.id"
from ss_user
<where>
<if test="loginName != null">
login_name=#{loginName}
</if>
<if test="name != null">
and name=#{name}
</if>
</where>
</select>
<!-- 插入用户: 1.由数据库生成id并赋值到user对象 2.输入用对象, 嵌套属性表达式#{team.id} -->
<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into ss_user (
login_name, name, password, email, team_id)
values (
#{loginName}, #{name}, #{password}, #{email},
#{team.id})
</insert>
<!-- 删除用户 -->
<delete id="delete" parameterType="int">
delete from ss_user where id=#{id}
</delete>
</mapper>
測試
@DirtiesContext
@ContextConfiguration(locations = { "/applicationContext.xml" })
public class UserMybatisDaoTest extends SpringTransactionalTestCase {
@Autowired
private UserMybatisDao userDao;
@Test
public void getUser() throws Exception {
User user = userDao.get(1L);
assertNotNull("User not found", user);
assertEquals("admin", user.getLoginName());
}
@Test
public void searchUser() throws Exception {
Map<String, Object> parameter = Maps.newHashMap();
parameter.put("name", "管理员");
List<User> result = userDao.search(parameter);
assertEquals(1, result.size());
assertEquals("admin", result.get(0).getLoginName());
}
@Test
public void createAndDeleteUser() throws Exception {
// create
int count = countRowsInTable("ss_user");
User user = UserData.randomUser();
userDao.save(user);
Long id = user.getId();
assertEquals(count + 1, countRowsInTable("ss_user"));
User result = userDao.get(id);
assertEquals(user.getLoginName(), result.getLoginName());
// delete
userDao.delete(id);
assertEquals(count, countRowsInTable("ss_user"));
assertNull(userDao.get(id));
}
}
总结
iBATIS 的着力点,则在于POJO与SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。相对Hibernate“O/R”而言,iBATIS 是一种“Sql Mapping”的ORM实现。
MyBatis的学习难度相对较低,但需要手写大量的sql语句,且不如Hibernate封装性强,适合使用在对于没有那么高的对象模型要求的项目。
具体与Hibernate的比较参考Hibernate与 MyBatis的比较
作者:Caos
链接:https://www.jianshu.com/p/61724b0989e1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
发表评论
-
解决hibernate向mysql插入中文乱码问题z
2013-04-02 23:10 11951、首先需要修改mysql数据库的配置文件my.ini,此文 ... -
org.hibernate.QueryException: query must begin with SELECT or FROM: delete
2013-03-31 21:55 1275转自:http://k0441258778983.iteye ... -
org.hibernate.exception.JDBCConnectionException:could not execute query原因及解决方法
2012-12-05 00:01 1688或者是报错:com.mysql.jdbc.exception ...
相关推荐
EzeOrm是一个由Java开发的轻量级ORM框架,其主要目标是简化数据库操作,将对象与关系数据库之间的映射关系自动化...对于想要学习ORM框架或对数据库操作有基本需求的Java开发者来说,EzeOrm是一个值得研究和尝试的工具。
这个简单的Java ORM框架,名为“Potato ORM”,可能是作者Li Hengming在业余时间创作的一个学习项目,用于消遣和教育目的。它具有极简的设计,包含的类数量不多,对于初学者来说,是理解ORM工作原理和实现方式的良好...
Java ORM(Object-Relational Mapping)是Java编程中用于数据库操作的一种技术,它将对象模型与关系数据库模型进行映射,使得程序员...学习和掌握ORM框架的使用,对于提升Java开发者的技能水平和工作效率具有重要意义。
本资源提供了一套基于Java的Elasticsearch搜索引擎ORM框架的设计源码,包含769个文件,其中包括321个JavaScript...这些文件非常适合用于学习和参考Java项目的开发,特别是对于那些需要高效搜索引擎和ORM框架的组织。
然而,重型的ORM框架虽然功能强大,但配置复杂,学习成本高,对小型和中型Web应用来说,可能过于臃肿。 针对这一问题,本文提出的Reflect-ORM框架是一个轻量级ORM解决方案。该框架基于Java的反射机制,并利用Java ...
随着信息技术的不断发展,企业对于数据的管理和操作需求日益增长。传统的JDBC(Java Database Connectivity)虽然可以实现...本项目旨在开发一个基于Java的简易ORM框架,为企业提供轻量级、高效的数据访问解决方案。
Java轻量级ORM框架是Java开发中的一个重要工具,它简化了数据库操作,使得开发者可以更加专注于业务逻辑,而不是繁琐的数据访问代码。ORM(Object-Relational Mapping)框架将对象模型与关系数据库模型进行映射,...
该项目是一个Elasticsearch搜索引擎框架的ORM框架,提供索引全自动智能托管功能,与Mybatis-plus一致的API,开发者只需要会MySQL语法即可完成对Es的相关操作,适合用于个人学习和实践Elasticsearch的开发技术。
在实际开发中,使用MongoDB ORM框架可以显著提升开发效率,因为它减少了对MongoDB原生驱动的学习成本,同时提供了更强大的类型安全和代码可维护性。然而,需要注意的是,虽然ORM能够简化很多工作,但在某些高级或...
通过学习如何使用和扩展JdbcTemplate,我们可以更好地理解ORM框架的设计原理,并为将来学习MyBatis等成熟的ORM框架打下坚实基础。 在实现自定义ORM框架的过程中,我们可能还会涉及到以下知识点: - **事务管理**:...
.NET ORM框架,全称为Object-Relational Mapping(对象关系映射)框架,是.NET开发者...在zc_orm框架这个压缩包中,可能包含了关于这些ORM框架的示例代码、教程或其他相关资源,可以帮助深入理解和学习.NET ORM的使用。
使用Java的注解来标记类和属性,以便ORM框架知道如何映射它们。例如,`@Table`注解用于标记表,`@Id`用于标记主键,`@Column`用于标记列。这允许ORM框架自动生成对应的SQL语句。 3. **数据访问对象(DAO)**: ...
本项目是基于Java开发的JFinal极速WEB+ORM框架设计源码,主要使用Java进行开发。项目共包含452个文件,其中Java源代码文件440个,JFinal框架配置文件4个,文本文件3个,XML配置文件2个,Git忽略配置文件1个,项目...
Java SSH 框架整合是Java开发中常见的一项技术,主要涉及...总之,Java SSH框架整合是Java Web开发中的基础技能,掌握这一技能能帮助开发者更好地应对复杂的业务场景,并为后续学习更先进的开发技术打下坚实的基础。
NHibernate是.NET平台下最知名和广泛使用的ORM框架之一,它的灵感来源于Java的Hibernate。NHibernate通过将.NET对象映射到数据库表,实现了对象和关系数据之间的透明转换。开发者定义实体类及其属性,然后创建相应的...
通过这个完整的Java Web开发教程,开发者不仅能够学习到JavaScript、DOM操作、Servlet、JSP等基础知识,还能掌握如Spring、Spring MVC等高级框架的使用,以及自定义ORM框架的设计,从而具备构建复杂企业级Web应用的...
通过这个“ORM框架-三层架构”的项目,你将能够学习到如何结合ORM框架与三层架构,实现一个完整的应用程序,理解各层间的交互方式,以及如何通过ORM框架高效地操作数据库。这将有助于提升你在实际项目开发中的能力,...
9. 学习和实践:理解ORM框架的基本原理,熟悉其API和配置,通过实际项目练习,可以帮助开发者更好地掌握ORM框架,并在实际开发中发挥其优势。 以上是对Java ORM框架的一般性介绍,具体到"Speed"框架,由于缺乏详细...
国内受欢迎的ORM框架通常需要具备高效、易用和兼容性好的特性,而标题中提到的框架,显然满足了这些需求。 该框架支持.NET Core,这是一个跨平台的开放源代码框架,用于构建各种类型的应用,包括桌面应用、Web应用...
Morphia是专门为MongoDB设计的一个强大的Java ORM框架,它提供了方便的方式来处理MongoDB文档与Java对象之间的转换。 首先,我们看到SpringMongoConfig类,这是Spring Data MongoDB的配置文件。这个类继承自...