(二)Mybatis介绍
Hibernate与Mybatis的区别
Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握, Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。适用于需求变化不多的项目,比如后台管理系统,OA等.
Mybatis:专注于sql本身,需要程序员自己编写sql语句,sql修改,优化比较方便,虽然程序员需要自己写sql,mybatis也可以实现映射(输入映射,输出映射)
Hibernate的开发难度要大于Mybatis。主要由于Hibernate比较复杂、庞大,学习周期较长。而Mybatis则相对简单一些,并且Mybatis主要依赖于sql的书写,让开发者感觉更熟悉。
原始dao的开发方法
(1)SqlSession的使用范围
- SqlsessionFactory创建SqlSession,使用单例模式管理工厂类
- SqlSession是一个面向用户(程序员)接口,SqlSession中提供了很多操作数据库的方法,SqlSession是线程不安全的,所以最佳应用场合在方法体内,定义成局部变量
(2)原始dao的开发方法(程序员需要写dao接口以及实现类)
//dao接口
import java.util.List;
import com.zb.entity.Student;
public interface StudentDao {
//通过id查询学生信息
public Student queryStudentById(int id);
//通过姓名模糊查询
public List<Student> queryStudentByName(String name);
//添加
public int insertStudent(Student student);
//修改
public int updateStudent(Student student);
//删除
public int deletStudent(int id);
}
//dao的实现类
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.zb.entity.Student;
public class StudentDaoImpl implements StudentDao {
//通过构造函数将我们SqlsessionFactory注入到这里
private SqlSessionFactory factory;
public StudentDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
//Sqlsession 是线程不安全的,他的应用场景是在方法中
@Override
public Student queryStudentById(int id) {
//第一步:创建sqlsession
SqlSession session = factory.openSession();
Student student = session.selectOne("test.queryStudentById",id);
//释放资源
session.close();
return student;
}
@Override
public List<Student> queryStudentByName(String name) {
SqlSession session = factory.openSession();
List<Student> students = session.selectList("test.queryStudentByName", "%张%");
session.close();
return students;
}
@Override
public int insertStudent(Student student) {
SqlSession session = factory.openSession();
int count = session.insert("test.addStudent",student);
//提交事务
session.commit();
//释放资源
session.close();
return count;
}
@Override
public int updateStudent(Student student) {
SqlSession session = factory.openSession();
int count = session.update("test.updateStudent", student);
//提交事务
session.commit();
//释放资源
session.close();
return count;
}
@Override
public int deletStudent(int id) {
SqlSession session = factory.openSession();
int count = session.delete("test.deleteStudentById",id);
session.commit();
session.close();
return count;
}
}
写一个工具类,创建单例的sqlSessionFactory对象
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class FactoryUtil {
private static SqlSessionFactory factory;
//synchronized 同步锁 同一时间只能一个线程调用这个方法
//线程安全 非线程安全
public static synchronized SqlSessionFactory getFactory() throws IOException {
if (factory == null) {
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
factory = new SqlSessionFactoryBuilder().build(is);
}
return factory;
}
}
主类测试
public class Test {
public static void main(String[] args) throws IOException {
//------------------------Dao层的查询------------------------------
SqlSessionFactory factory = FactoryUtil.getFactory();//先获得一个
StudentDao studentDao = new StudentDaoImpl(factory);
Student student = studentDao.queryStudentById(3);
System.out.println(student);
//---------------------------------------------------------------
//------------------------Dao层的插入------------------------------
SqlSessionFactory factory = FactoryUtil.getFactory();
StudentDao studentDao = new StudentDaoImpl(factory);
Student student2 = new Student();
student2.setName("2222");
student2.setAge(18);
student2.setClass_id(2);
System.out.println(studentDao.insertStudent(student2));
//---------------------------------------------------------------
//------------------------Dao层的修改(需要结合查询)------------------------------
SqlSessionFactory factory = FactoryUtil.getFactory();
StudentDao studentDao = new StudentDaoImpl(factory);
Student student = studentDao.queryStudentById(10);
student.setClass_id(21);
System.out.println(studentDao.updateStudent(student));
//---------------------------------------------------------------
//------------------------Dao层的删除------------------------------
SqlSessionFactory factory = FactoryUtil.getFactory();
StudentDao studentDao = new StudentDaoImpl(factory);
studentDao.deletStudent(6);
//---------------------------------------------------------------
}
}
(3)总结:
1、接口Dao
2、实现类Impl
3、Sqlsessionfactory单例的工具类
4、测试
缺点 :代码重复;错误无法在编译时发现
因为这些缺点,所以引出 mapper代理
Mapper代理的方法(程序员只需要写Mapper的接口,Mapper来当作接口实现类)
- Mapper.xml中,namespace要等于mapper接口的地址
- 接口中的方法名等于mapper.xml中标签statement的id
- Mapper接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致
- Mapper接口中的方法数返回值类型和mapper.xml中statement的resultType指定的类型一致
- Mapper.xml起名规范:实体类名(如Student)+Mapper.xml
Mapper代理的好处 :
代码不重复;错误在编译时能知道~比如:参数是object类型对象,在传统中dao开发中参数不对应检测不出来,只能在运行的时候检测出来;用了mapper之后,只要参数类型不对应,在编译的时候就能查出来,就会提示错误。
创建一个StudentMapper接口,此接口就相当于dao接口
public interface StudentMapper {
//通过id查询学生信息
public Student queryStudentById(int id);
//通过姓名模糊查询
public List<Student> queryStudentByName(String name);
//添加
public int addStudent(Student student);
//修改
public int updateStudent(Student student);
//删除
public int deletStudentById(int id);
}
编写StudentMapper.xml
需要注意的地方,图片中已表明。
主类测试
public class Test {
public static void main(String[] args){
//使用mapper代理的方式开发
SqlSessionFactory sessionFactory = FactoryUtil.getFactory();
//开启session
SqlSession session = sessionFactory.openSession();
//生成mapper接口实现类的代理对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//-------------------查询-----------------------
Student student = mapper.queryStudentById(2);
System.out.println(student);
//-------------------增加-----------------------
Student student = new Student();
student.setName("啊啊");
student.setAge(18);
student.setClass_id(2);
mapper.addStudent(student);
session.commit();
session.close();
}
Mapper代理对象内部调用selectOne或者SelectList
1) 如果mapper方法返回的是单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库
2) 如果mapper方法返回的集合对象,代理对象内部通过SelectList查询数据库
Mapper接口方法参数只能有一个是否影响系统开发?
mapper接口只能有一个参数,因为在xml中单个标签只能有一个parameterType,所以就限定了方法中的参数中能有一个。
解决办法:就是使用包装类,即包装类的属性包括其他类的对象或其他类型的数据。包装类其实就是给原来的类增添一些属性,如现有一个学生类,属性有姓名name,id,班级class_id,年龄age等,那么创建一个新的类叫做Studentgrade,它继承了student,额外的属性有分数等等。
Mapper 是如何实现的不用实现类的?
通过namespace 反射 得到 接口 ,通过接口中的方法名去找标签中的id,找到了对应的id,就去执行标签中的操作。根据返回类型 是pojo 还是集合 来决定是用selectOne还是selectList。
Mybatis-config.xml文件
对于java项目,在Mybatis-config.xml中必须要配置的有2个
(1) 数据源
(2)加载xml文件
(1)数据源
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理,事务由mybatis控制 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池,由mybatis管理 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
注意:
如果不加第一句话,那么你必须把内容写在mybatis-config.xml中,建议还是加上第一句话,即把数据库连接放在properties里,这样体现了功能分离的特点,以后修改也方便。
附上properties文件:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/数据库名?characterEncoding=UTF-8
jdbc.username=填写自己的账号名
jdbc.password=填写自己的密码
(2)加载xml文件
有两种方式:
- 第一种就是非Mapper开发
- 第二种就是Mapper开发
<mappers>
<!-- 第一种 填写相对路径 非Mapper开发的xml的名字没有要求-->
<mapper resource="student.xml" />
<!-- 第二种:
使用规范:
1、mapper接口的名字要与mapper.xml文件的名字保持一致,并且在同一个文件夹中
2、必须使用mapper代理的方式开发!
-->
<mapper class="com.zb.Mapper.StudentMapper"/>
</mappers>
Mybatis-config.xml文件中还可以写的内容
(1)定义别名
意思就是:在放sql语句的xml中,parameterType和resultType需要写全路径名,有了别名之后就可以直接写别名。
定义别名分2种:
- 为某个类单独定义别名
- 为包底下的所以的类定义别名,即批量定义别名
<!-- 定义别名 -->
<typeAliases>
<!-- 单独定义别名 -->
<!-- <typeAlias type="com.zb.entity.Student" alias="student"/> -->
<!-- 批量定义别名 -->
<!-- package 是扫描的包的名字
自动扫描指定的包,为包下的类去自动设置别名,别名就是类名 大小写无所谓
-->
<package name="com.zb.entity"/>
</typeAliases>
补充:
其实int,double,String在放sql语句的xml中也需要写全路径名,但是它们是mybatis默认支持的别名。(mybatis默认支持的别名如下图)
上一篇: Mysql服务器的启动与停止(一)
下一篇: VFP与其他应用程序的集成_php基础