☃ 数据库准备
create table tb_student
(
Sno varchar(20),
Sname varchar(50),
primary key (Sno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table tb_course
(
Cno varchar(20),
Cname varchar(50),
Tno varchar(20),
primary key (Cno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table tb_sc
(
Sno varchar(20),
Cno varchar(20),
tb_score int,
primary key (Sno,Cno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table tb_teacher
(
Tno varchar(20),
Tname varchar(50),
primary key (Tno)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tb_student`(Sno,Sname) VALUES ('001','陈一');
INSERT INTO `tb_student`(Sno,Sname) VALUES ('002','郭二');
INSERT INTO `tb_student`(Sno,Sname) VALUES ('003','张三');
INSERT INTO `tb_student`(Sno,Sname) VALUES ('004','李四');
INSERT INTO `tb_student`(Sno,Sname) VALUES ('005','王五');
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('001','张老师');
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('002','王老师');
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('003','钱老师');
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('004','刘老师');
INSERT INTO `tb_teacher`(Tno,Tname) VALUES ('005','胡老师');
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('001','语文','张老师');
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('002','数学','王老师');
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('003','英语','钱老师');
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('004','物理','刘老师');
INSERT INTO `tb_course`(Cno,Cname,Tno) VALUES ('005','政治','胡老师');
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','001',50);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','002',60);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','003',70);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','004',80);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('001','005',90);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','001',90);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','002',80);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','003',70);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','004',60);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('002','005',50);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','001',81);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','002',82);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','003',83);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','004',84);
INSERT INTO `tb_sc`(Sno,Cno,tb_score) VALUES ('003','005',85);
JDBC方式
- 1.创建maven工程
- 2.引入mysql驱动依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
- 3.准备数据库数据
- 4.JdbcTest.class
public static void main(String[] args) throws Exception {
Connection connection = null;
PreparedStatement prepareStatement = null;
ResultSet rs = null;
try {
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
String url = "jdbc:mysql://localhost:3306/test_mybatis?useSSL=false";
String user = "root";
String password = "root";
connection = DriverManager.getConnection(url, user, password);
// 3.获取statement||preparedStatement
String sql = "select * from tb_student where sno=?";
prepareStatement = connection.prepareStatement(sql);
// 4.设置参数
prepareStatement.setLong(1, "001"); //从1开始不是0
// 5.执行查询
rs = prepareStatement.executeQuery();
// 6.处理结果集
while (rs.next()) {
System.out.println(rs.getString("sno"));
System.out.println(rs.getString("sname"));
}
} finally {
// 7.关闭连接,释放资源
if (rs != null) {
rs.close();
}
if (prepareStatement != null) {
prepareStatement.close();
}
if (connection != null) {
connection.close();
}
}
}
- ☃ JDBC缺点
1.四大金刚硬编码;SQL与代码耦合
2.每次都要加载驱动,获得/关闭connection/statement/resultSet
3.传参需要判断参数类型和下标,结果集获得结果需要判断类型和下标/列名
❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤
MyBatis
- MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records.
- Object relational mapping
- 对JDBC的简单封装:JDBC > MyBatis > Hibernate
- 官方文档
[英文]http://www.mybatis.org/mybatis-3/index.html
[中文]http://www.mybatis.org/mybatis-3/zh/index.html
🍀 MyBaits整体架构
A.根据创建SqlSessionFactory的方式,分成xml和annotation两种方式:
1.xml方式需要mybatis-cofig.xml和xxMapper.xml(映射文件也可以用注解代替,但复杂SQL仍需在xxMapper.xml中书写)
2.注解方式要全注解,读不到mybatis-cofig.xml和xxxMapper.xml???(待确认)
B.创建SqlSessionFactory和SqlSession后,根据[接口方法-Statement]的映射方式,又可分成一般和动态代理两种方式:
1.一般方式容易出现字符串(namespace.statementId)拼写错误
2.动态代理方式推荐使用,但须遵循4个约定
🍀 HelloWorld(3版)
①HelloWorld(最简xml版)
- ☞ 引入依赖(pom.xml)
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- mybatis支持了slf4j日志系统 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
- ☞ log4j.properties和jdbc.properties
log4j.rootLogger=DEBUG,A1
log4j.logger.org.apache=DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_mybatis?useSSL=false
username=root
password=root
- ☞ 全局配置文件(mybatis-config.xml)
<?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>
<!-- 读取jdbc四大金刚 -->
<properties resource="jdbc.properties"></properties>
<!-- 环境配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
- ☞ POJO和Mapper.xml
public class Student {
private String sno;
private String sname;
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Student [sno=" + sno + ", sname=" + sname + "]";
}
}
<?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="123">
<select id="selectById" resultType="com.top.test.pojo.Student">
select * from tb_student where sno = #{sno}
</select>
</mapper>
- ☞ 测试类
public class MybatisTest {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Student student = sqlSession.selectOne("123.selectById", "001");
System.out.println(student);
} finally {
sqlSession.close();
}
}
}
如果这里用SqlSession sqlSession = sqlSessionFactory.openSession(true);(重载方法,会自动提交),那么做增删改时不用手动提交了:this.sqlSession.commit();
注:简单版的helloworld没有写Mapper接口和实现类
- ☞ 输出
2017-09-26 12:10:34,623 [main] [org.apache.ibatis.logging.LogFactory]-[DEBUG] Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,707 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2017-09-26 12:10:34,763 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2017-09-26 12:10:34,954 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 239465106.
2017-09-26 12:10:34,954 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:34,956 [main] [123.selectById]-[DEBUG] ==> Preparing: select * from tb_student where sno = ?
2017-09-26 12:10:34,987 [main] [123.selectById]-[DEBUG] ==> Parameters: 001(String)
2017-09-26 12:10:35,001 [main] [123.selectById]-[DEBUG] <== Total: 1
Student [sno=001, sname=陈一]
2017-09-26 12:10:35,001 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:35,002 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@e45f292]
2017-09-26 12:10:35,002 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 239465106 to pool.
- ☞ 目录结构
☃ 分析
- A.步骤
- 1.配置mybatis-config.xml 全局的配置文件 (1.数据源,2.xxxMapper.xml)
- 2.创建SqlSessionFactory,打开SqlSession
- 4.通过SqlSession操作数据库 CRUD
- 5.调用sqlSession.commit()提交事务(增删改)
- 6.调用sqlSession.close()关闭会话
- B.注意事项
- 1.这里直接使用SqlSession进行CRUD,可以使用Mapper接口和其实现类,也可以使用动态代理方式(底层都是sqlSession)
- 2.因为没有写Mapper接口,映射文件的namespace名和statementId是随意取得,只要保证namespace唯一&&该namespace下该id唯一即可。一般与Mapper接口一致
②HelloWorld(完整xml版)
完整版在最简版的基础上:a.删除了测试类;b.增加Mapper接口和实现类;c.使用单元测试用例
- ☞ 导入Junit4依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
- ☞ Mapper接口和实现类
public interface StudentMapper {
//@Select("select * from tb_student where sno = #{sno}")
public Student selectById(String sno);
public List<Student> selectAll();
public int selectCount();
//CUD默认返回int(影响条数,batchUpdate似乎不行),直接Integer接收不需要设置resultType;若要返回主键需要Statement标签中设置
public Integer insertStu(Student student);
public Integer updateStu(Student student);
public void deleteById(String sno);
}
public class StudentMapperImpl implements StudentMapper {
public SqlSession sqlSession;
//有参构造
public StudentMapperImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public Student selectById(String sno) {
return this.sqlSession.selectOne("123.selectById", sno);
}
public List<Student> selectAll() {
return this.sqlSession.selectList("123.selectAll");
}
public int selectCount() {
return this.sqlSession.selectOne("123.selectCount");
}
//CUD默认返回int(影响条数),要返回主键需要Statement标签中设置
public Integer insertStu(Student student) {
return this.sqlSession.insert("123.insert", student);
}
public Integer updateStu(Student student) {
return this.sqlSession.update("123.update", student);
}
public void deleteById(String sno) {
this.sqlSession.delete("123.delete", sno);
}
}
☞ StudentMapper.xml
<?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="123">
<select id="selectById" resultType="com.top.test.pojo.Student">
select * from tb_student where sno = #{sno};
</select>
<select id="selectAll" resultType="com.top.test.pojo.Student">
select * from tb_student;
</select>
<!-- 返回int/integer都可以,integer也可以用int接,但一般对应起来都用integer -->
<select id="selectCount" resultType="Integer">
select count(*) from tb_student;
</select>
<insert id="insert" parameterType="com.top.test.pojo.Student">
insert into tb_student(
sno,
sname
)
values(
#{sno},
#{sname}
);
</insert>
<update id="update" parameterType="com.top.test.pojo.Student">
upadte tb_student set
sname = #{sname}
where
sno = #{sno};
</update>
<delete id="delete" parameterType="String">
delete from tb_student where sno = #{sno};
</delete>
</mapper>
这里的namespace和id是随意取得,一般和Mapper接口方法一致;使用动态代理时必须要一致
- ☞ 测试用例
public class StudentMapperTest {
public SqlSession sqlSession;
public StudentMapper studentMapper;
@Before
//@Test方法执行前执行该方法
public void setUp() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//不能自己加SqlSession sqlSession(加了就是局部变量,setUp执行后就没了),所以要使用成员变量,不能自己new一个引用
this.sqlSession = sqlSessionFactory.openSession();
//同上;通过有参构造传入sqlSession
this.studentMapper = new StudentMapperImpl(sqlSession);
}
@Test
public void testSelectById() {
Student student = this.studentMapper.selectById("001");
System.out.println(student);
}
@Test
public void testSelectAll() {
List<Student> list = this.studentMapper.selectAll();
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void testSelectCount() {
int count = this.studentMapper.selectCount();
System.out.println(count);
}
@Test
public void testInsertStu() {
Student student = new Student();
student.setSno("006");
student.setSname("周六");
Integer count = this.studentMapper.insertStu(student);
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
System.out.println(count);
}
@Test
public void testUpdateStu() {
Student student = new Student();
student.setSno("006");
student.setSname("赵七");
Integer count = this.studentMapper.updateStu(student);
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
System.out.println(count);
}
@Test
public void testDeleteById() {
this.studentMapper.deleteById("006");
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
}
}
sqlSession本身的两个实现类是线程不安全的(mybatis-spring提供的SqlSessionTemplate是线程安全的实现),所以这里生命周期最好是一个线程(如一次request-response)/方法内-局部变量
- ☞ 目录结构
☃ 分析执行顺序
- 1.Run as,会先执行@Before方法
- 创建sqlSession(加载主配置文件mybatis-config.xml),赋值给成员变量引用
- 实例StudentMapperImpl(传sqlSession)
- 2.再执行StudentMapperTest中的@Test方法
- 测试StudentMapperImpl的方法-->也就是sqlSession执行相应的statement
- 3.提交,数据库更新
③HelloWorld(动态代理版)
因为在DAO/Mapper的实现类中对sqlsession的使用方式很类似。mybatis提供了接口的动态代理(懒)
- 在②中:接口->实现类->mapper.xml,在实现类中完成接口方法和statement的对应(namespace.id);
- 接口的动态代理版不写实现类,如何完成接口方法和statement的对应:
四个约定
➢ namespace名称 = 接口Mapper/DAO的全类名
➢ statement的id = 接口Mapper/DAO的方法名
➢ resultType = mapper接口方法的返回类型
➢ parameterType = mapper接口方法的参数类型(也可省略不写)
使用动态代理改造CRUD
a.删除DAO/Mapper接口实现类;b.满足四个约定;c.修改单元测试(接口方法-Statement的映射方式)
- ☞ 修改后的StudentMapper.xml(namespace和statementId)
<?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.top.test.mapper.StudentMapper">
<select id="selectById" resultType="com.top.test.pojo.Student">
select * from tb_student where sno = #{sno};
</select>
<select id="selectAll" resultType="com.top.test.pojo.Student">
select * from tb_student;
</select>
<!-- 返回int/integer都可以,integer也可以用int接,但一般对应起来都用integer -->
<select id="selectCount" resultType="Integer">
select count(*) from tb_student;
</select>
<insert id="insertStu" parameterType="com.top.test.pojo.Student">
insert into tb_student(
sno,
sname
)
values(
#{sno},
#{sname}
);
</insert>
<update id="updateStu" parameterType="com.top.test.pojo.Student">
upadte tb_student set
sname = #{sname}
where
sno = #{sno};
</update>
<delete id="deleteById" parameterType="String">
delete from tb_student where sno = #{sno};
</delete>
</mapper>
- ☞ 修改后的测试用例
public class StudentMapperTest {
public SqlSession sqlSession;
public StudentMapper studentMapper;
@Before
//@Test方法执行前执行该方法
public void setUp() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//不能自己加SqlSession sqlSession(加了就是局部变量,setUp执行后就没了),所以要使用成员变量,不能自己new一个赋给引用
this.sqlSession = sqlSessionFactory.openSession();
//this.studentMapper = new StudentMapperImpl(sqlSession);//同上;通过有参构造传入sqlSession
//同上;动态代理方式
this.studentMapper = this.sqlSession.getMapper(StudentMapper.class);
}
@Test
public void testSelectById() {
Student student = this.studentMapper.selectById("001");
System.out.println(student);
}
@Test
public void testSelectAll() {
List<Student> list = this.studentMapper.selectAll();
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void testSelectCount() {
int count = this.studentMapper.selectCount();
System.out.println(count);
}
@Test
public void testInsertStu() {
Student student = new Student();
student.setSno("006");
student.setSname("周六");
Integer count = this.studentMapper.insertStu(student);
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
System.out.println(count);
}
@Test
public void testUpdateStu() {
Student student = new Student();
student.setSno("006");
student.setSname("赵七");
Integer count = this.studentMapper.updateStu(student);
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
System.out.println(count);
}
@Test
public void testDeleteById() {
this.studentMapper.deleteById("006");
this.sqlSession.commit();//不提交不生效;使用重载方法可以自动提交事务
}
}
-
☞ 目录结构
-
数据库字段名和实体类属性名不一致
- 语句中使用别名
- resultMap中对应
- mybatis-config.xml中开启驼峰匹配(约定)