一、jdbc开发程序:
1.package cn.itcast.jdbc.test;
2.
3.import java.sql.Connection;
4.import java.sql.DriverManager;
5.import java.sql.PreparedStatement;
6.import java.sql.ResultSet;
7.import java.sql.SQLException;
8.
9.public class JDBCTest {
10.
11. public static void main(String[] args) {
12. Connection connection = null;
13. PreparedStatement preparedStatement = null;
14. ResultSet resultSet = null;
15. try {
16. //加载数据库驱动
17. Class.forName("com.mysql.jdbc.Driver");
18.
19. //通过驱动管理类获取数据库链接
20. connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis01?characterEncoding=utf-8", "root", "admin");
21. //定义sql语句 ?表示占位符
22. String sql = "select * from user where username = ?";
23. //获取预处理statement
24. preparedStatement = connection.prepareStatement(sql);
25. //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
26. preparedStatement.setString(1, "王五");
27. //向数据库发出sql执行查询,查询出结果集
28. resultSet = preparedStatement.executeQuery();
29. //遍历查询结果集
30. while(resultSet.next()){
31. System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
32. }
33. } catch (Exception e) {
34. e.printStackTrace();
35. }finally{
36. //释放资源
37. if(resultSet!=null){
38. try {
39. resultSet.close();
40. } catch (SQLException e) {
41. // TODO Auto-generated catch block
42. e.printStackTrace();
43. }
44. }
45. if(preparedStatement!=null){
46. try {
47. preparedStatement.close();
48. } catch (SQLException e) {
49. // TODO Auto-generated catch block
50. e.printStackTrace();
51. }
52. }
53. if(connection!=null){
54. try {
55. connection.close();
56. } catch (SQLException e) {
57. // TODO Auto-generated catch block
58. e.printStackTrace();
59. }
60. }
61.
62. }
63.
64. }
65.
66.
67.}
二、Mybatis优化:
1、第一步优化:连接获取和释放
问题描述:
数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能。
解决问题:
数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题。通过连接池就可以反复利用已经建立的连接去访问数据库了。减少连接的开启和关闭的时间。
问题描述:
但是现在连接池多种多样,可能存在变化,有可能采用DBCP的连接池,也有可能采用容器本身的JNDI数据库连接池。
解决问题:
我们可以通过DataSource进行隔离解耦,我们统一从DataSource里面获取数据库连接,DataSource具体由DBCP实现还是由容器的JNDI实现都可以,所以我们将DataSource的具体实现通过让用户配置来应对变化。
2、第二步优化:SQL存取
问题描述:
我们使用JDBC进行操作数据库时,SQL语句基本都散落在各个JAVA类中,这样有三个不足之处:
第一,可读性很差,不利于维护以及做性能调优。
第二,改动Java代码需要重新编译、打包部署。
第三,不利于取出SQL在数据库客户端执行(取出后还得删掉中间的Java代码,编写好的SQL语句写好后还得通过+号在Java进行拼凑)。
解决问题:
我们可以考虑不把SQL语句写到Java代码中,那么把SQL语句放到哪里呢?首先需要有一个统一存放的地方,我们可以将这些SQL语句统一集中放到配置文件或者数据库里面(以key-value的格式存放)。然后通过SQL语句的key值去获取对应的SQL语句。
既然我们将SQL语句都统一放在配置文件或者数据库中,那么这里就涉及一个SQL语句的加载问题。
3、第三步优化:传入参数映射和动态SQL
问题描述:
很多情况下,我们都可以通过在SQL语句中设置占位符来达到使用传入参数的目的,这种方式本身就有一定局限性,它是按照一定顺序传入参数的,要与占位符一一匹配。但是,如果我们传入的参数是不确定的(比如列表查询,根据用户填写的查询条件不同,传入查询的参数也是不同的,有时是一个参数、有时可能是三个参数),那么我们就得在后台代码中自己根据请求的传入参数去拼凑相应的SQL语句,这样的话还是避免不了在Java代码里面写SQL语句的命运。既然我们已经把SQL语句统一存放在配置文件或者数据库中了,怎么做到能够根据前台传入参数的不同,动态生成对应的SQL语句呢?
解决问题:
Mybatis自动将Java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型
4、第四步优化:结果映射和结果缓存
问题描述:
执行SQL语句、获取执行结果、对执行结果进行转换处理、释放相关资源是一整套下来的。假如是执行查询语句,那么执行SQL语句后,返回的是一个ResultSet结果集,这个时候我们就需要将ResultSet对象的数据取出来,不然等到释放资源时就取不到这些结果信息了。我们从前面的优化来看,以及将获取连接、设置传入参数、执行SQL语句、释放资源这些都封装起来了,只剩下结果处理这块还没有进行封装,如果能封装起来,每个数据库操作都不用自己写那么一大堆Java代码,直接调用一个封装的方法就可以搞定了。
解决问题:
我们分析一下,一般对执行结果的有哪些处理,有可能将结果不做任何处理就直接返回,也有可能将结果转换成一个JavaBean对象返回、一个Map返回、一个List返回等等,结果处理可能是多种多样的。从这里看,我们必须告诉SQL处理器两点:第一,需要返回什么类型的对象;第二,需要返回的对象的数据结构怎么跟执行的结果映射,这样才能将具体的值copy到对应的数据结构上。
接下来,我们可以进而考虑对SQL执行结果的缓存来提升性能。缓存数据都是key-value的格式,那么这个key怎么来呢?怎么保证唯一呢?即使同一条SQL语句几次访问的过程中由于传入参数的不同,得到的执行SQL语句也是不同的。那么缓存起来的时候是多对。但是SQL语句和传入参数两部分合起来可以作为数据缓存的key值。
5、第五步优化:解决重复SQL语句问题
问题描述:
由于我们将所有SQL语句都放到配置文件中,这个时候会遇到一个SQL重复的问题,几个功能的SQL语句其实都差不多,有些可能是SELECT后面那段不同、有些可能是WHERE语句不同。有时候表结构改了,那么我们就需要改多个地方,不利于维护。
解决问题:
当我们的代码程序出现重复代码时怎么办?将重复的代码抽离出来成为独立的一个类,然后在各个需要使用的地方进行引用。对于SQL重复的问题,我们也可以采用这种方式,通过将SQL片段模块化,将重复的SQL片段独立成一个SQL块,然后在各个SQL语句引用重复的SQL块,这样需要修改时只需要修改一处即可。
6、优化总结:
(1) 使用数据库连接池对连接进行管理
(2) SQL语句统一存放到配置文件
(3) SQL语句变量和传入参数的映射以及动态SQL
(4) 动态SQL语句的处理
(5) 对数据库操作结果的映射和结果缓存
三、Mybatis简介:
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回
Mybatis是apache旗下的顶级一个项目,mybatis是对jdbc的封装的一个持久层框架
mybatis配置
1、SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
2、mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
3、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
4、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
5、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
6、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
7、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
8、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
四、入门程序
1、需求:
1、根据用户主见进行查询用户信息
2、根据用户名称模糊查询用户信息
3、添加用户
4、删除用户
5、更新用户
2、导入jar包
3、创建sqlMapConfig.xml
a、基本配置
<?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:配置mybatis基础支持层环境开始标签 -->
<configuration>
<!-- environments:mybatis连接数据库环境,可以配置多个缓存,开发环境,测试缓存 -->
<environments default="development">
<!-- environment:开发环境 -->
<environment id="development">
<!-- 事物,使用jdbc事物 -->
<transactionManager type="JDBC" />
<!-- 数据源 连接池POOLED -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.66.66:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="admin" />
</dataSource>
</environment>
</environments>
<!-- 引入第三方映射文件,类似hibernate中hbm -->
<mappers>
<mapper resource="sqlMap/UserMapper.xml" />
</mappers>
</configuration>
b、可以配置外部引用配置文件
<configuration>
<properties resource="jdbc.properties" ></properties>
<!-- environments:mybatis连接数据库环境,可以配置多个缓存,开发环境,测试缓存 -->
<environments default="development">
<!-- environment:开发环境 -->
<environment id="development">
<!-- 事物,使用jdbc事物 -->
<transactionManager type="JDBC" />
<!-- 数据源 连接池POOLED -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 引入第三方映射文件,类似hibernate中hbm -->
<mappers>
<mapper resource="sqlMap/UserMapper.xml" />
</mappers>
</configuration>
c、mybatis别名定义
定义单个别名:
一般都是给pojo对象定义别名,且每个对象定义一个别名
<!-- 给每个pojo对象定义一个别名 -->
<typeAliases>
<typeAlias type="com.itcast.domain.User" alias="user" />
<!--
type:指定定义别名的类型
alias:指定别名的名称
-->
</typeAliases>
在properties和environments标签之间
批量扫描方法定义别名
package:扫描定义别名所在的包路径即可, 此包路径下的所有类自动被定义别名,别名名称:类名,大小写随意
<typeAliases>
<package name="com.itcast.domain"/>
</typeAliases>
d、其他属性设置值:
properties 引入外部资源文件
settings?, // 设置缓存,查询缓存,二级缓存
typeAliases?, //定义别名
typeHandlers?, //把数据库类型(Varchar)转换java类型(String) TypeHandlerRegistry
objectFactory?,//对象工厂,创建返回值对象
objectWrapperFactory?,
reflectorFactory?,
plugins?, //配置插件(分页插件)
environments?,(配置环境,数据源,事物)
databaseIdProvider?,
mappers?) //引入外部映射mapper文件
5、创建实体类(javabean)
6、创建映射配置文件UserMapper.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:映射文件开始标签,标签中封装所有sql语句
namespace:
1,命名空间,唯一标识一个映射文件,名称自定义
2,如果是接口代理开发,namespace有特殊含义,不能随便定义
-->
<mapper namespace="test">
<!--
需求:查询所有用户
select:用来封装select查询语句标签
id:sql语句唯一标识
resultType:指定返回值类型,无论返回是pojo,集合类型,都返回的是pojo对象类型
-->
<select id="findAllUser" resultType="com.itheima.domain.User">
select * from user
</select>
<!--
需求:根据id查询用户
select:用来封装select查询语句标签,所有查询语句都必须使用select标签封装
id:sql语句唯一标识
parameterType:指定参数类型,通常使用别名定义 (int就是别名)
resultType:指定返回值类型
#{}:占位符,mybatis框架默认自动加上?,#{}自动获取传递参数
特点:如果传递参数是基本类型,#{}括号里面可以是任意值
-->
<select id="findUserWithId" parameterType="int" resultType="com.itheima.domain.User">
select * from user where id=#{张三丰}
</select>
<!--
需求:根据用户名模糊查询用户(查询姓张的用户)
select:用来封装select查询语句标签,所有查询语句都必须使用select标签封装
id:sql语句唯一标识
parameterType:指定参数类型,通常使用别名定义 (string就是别名)
resultType:指定返回值类型
#{}:mybatis框架自动提供特殊模式进行模糊查询语句组装:"%"#{username}"%"=='%张%'
特点:
1,"%"#{username}"%" 是mybatis提供特殊写法,自动构造模糊查询
2,如果传递数据是字符串类型,#{username}获取的数据自动加上双引号 "张"
3,如果传递参数是基本类型,#{}可以是任意值
4,如果传递参数是pojo对象,#{}使用ognl(对象导航语言)表达式获取参数
ognl语法:属性.属性.属性...
${}:sql语句拼接:'%${value}%'
1,${}获取数据无论是什么类型数据,原样获取,不加任何修饰
2,${}如果获取是基本类型(int,long,string)的数据,${}里面必须是value
3,如果传递参数是pojo对象,#{}使用ognl(对象导航语言)表达式获取参数
4,${}就是sql拼接,会有sql语句注入风险,一般使用#
-->
<select id="findUserWithUsername" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username LIKE "%"#{username}"%"
</select>
<!--
需求:添加用户
insert:用来封装insert插入语句标签
id:sql语句唯一标识
parameterType:指定传递参数类型
传递参数:pojo类型,获取参数时候使用ognl表达式获取
-->
<insert id="insertUser" parameterType="com.itheima.domain.User" useGeneratedKeys="true" keyProperty="id">
<!--
selectKey:用来返回主键标识标签
keyProperty:标识返回注解对应javabean设置属性
order:返回策略,id是在sql语句执行之后生成,因此使用AFTER
resultType:指定返回值类型
-->
<!-- <selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID()
</selectKey> -->
insert into user values(#{id},
#{username},
#{birthday},
#{sex},
#{address})
</insert>
<!--
需求:根据id修改用户
-->
<update id="updateUserByID" parameterType="com.itheima.domain.User">
update user set username=#{username},
birthday=#{birthday},
sex=#{sex},
address=#{address}
where id=#{id}
</update>
<!-- 需求:根据id删除用户 -->
<delete id="deleteUserByID" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
7、测试类
package com.itheima.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
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.Before;
import org.junit.Test;
import com.itheima.domain.User;
public class MybatisTest {
SqlSessionFactory sqlSessionFactory=null;
/**
* 需求:抽取公共代码
* @throws Exception
*/
@Before
public void beforeConfig() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取sqlSessionFacory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 需求:查询所有用户
* @throws Exception
*/
@Test
public void test01() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取sqlSessionFacory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从工厂中获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用sqlSession接口方法
//找到Mapped映射文件中sql语句执行
//如何唯一定位一条sql语句-----namespace.id
List<User> list = sqlSession.selectList("test.findAllUser");
System.out.println(list);
}
/**
* 需求:根据id查询用户
* 参数:int id
* 返回值:User
* @throws Exception
*/
@Test
public void test02() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//通过工厂构建类,获取工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从工厂中获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用接口查询方法
//返回值只有一个对象,selectOne
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
User user = sqlSession.selectOne("test.findUserWithId", 30);
System.out.println(user);
}
/**
* 需求:根据用户名进行模糊查询
*/
@Test
public void test03(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
List<User> list = sqlSession.selectList("test.findUserWithUsername", "张");
System.out.println(list);
}
/**
* 需求:保存用户数据
*/
@Test
public void test04(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建user对象,封装保存数据
User user = new User();
user.setUsername("赵敏3");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("大都3");
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.insert("test.insertUser", user);
//返回保存user对象主键,把主键值设置order对象外键中,订单和user关系,必须手动维护
System.out.println("user对象主键:"+user.getId());
//提交事物
sqlSession.commit();
System.out.println("user对象主键:"+user.getId());
}
/**
* 需求:保存用户数据
*/
@Test
public void test05(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建user对象,封装保存数据
User user = new User();
user.setId(34);
user.setUsername("周芷若");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("峨眉山");
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.update("test.updateUserByID", user);
//提交事物
sqlSession.commit();
}
/**
* 需求:保存用户数据
*/
@Test
public void test06(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.delete("test.deleteUserByID", 33);
//提交事物
sqlSession.commit();
}
}
#{}:mybatis框架自动提供特殊模式进行模糊查询语句组装:"%"#{username}"%"=='%张%'
特点:
1,"%"#{}"%" 是mybatis提供特殊写法,自动构造模糊查询
2,如果传递数据是字符串类型,#{}获取的数据自动加上双引号 "张"
3,如果传递参数是基本类型,#{}括号里面的内容可以是任意值
4,如果传递参数是pojo对象,#{}括号里面的内容使用ognl(对象导航语言)表达式获取参数
ognl语法:属性.属性.属性...
${}:sql语句拼接:'%${value}%'
1,${}获取数据无论是什么类型数据,原样获取,不加任何修饰
2,${}如果获取是基本类型(int,long,string)的数据,${}括号里面必须是value
3,如果传递参数是pojo对象,${}括号里面使用ognl(对象导航语言)表达式获取参数
4,${}就是sql拼接,会有sql语句注入风险,一般使用#
一、select:
1、查询所有用户
<select id="findAllUser" resultType="com.itcast.domain.User" >
select * from user
</select>
public void test01() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findAllUser");
System.out.println(list);
}
2、根据id查询用户
此时#{}括号里面内容可以写任何字符,但是如果要用${}里面只能写value
<select id="findUserWithId" parameterType="int" resultType="com.itcast.domain.User" >
select * from user where id=#{id}
</select>
public void test2() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithId",26);
System.out.println(user);
}
因为id是int型的,所以不能加双引号
3、根据username准确查询用户
<select id="findUserWithUsername" parameterType="string" resultType="com.itcast.domain.User" >
select * from user where username="${value}"
</select> -->
此时username=的参数必须要加双引号,所以${}两边加了双引号
而如果是#{},两边就不用加双引号的,因为已经自动加了
public void test3() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithUsername","王五");
System.err.println(user);
}
4、根据username模糊查询
<select id="findUserWithUsername" parameterType="string" resultType="com.itcast.domain.User" >
select * from user where username like "%${value}%"
</select>
public void test4() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserWithUsername","王五");
System.out.println(list);
}
二、insert
1、普通没有返回值添加用户
<insert id="insertUser" parameterType="com.itcast.domain.User" >
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
public void test5() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setBirthday(new Date());
user.setSex("男");
user.setUsername("秦贺");
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
}
2、返回id的添加
<insert id="insertUser" parameterType="com.itcast.domain.User" >
<selectKey keyProperty="id" order="AFTER" resultType="int" >
SELECT LAST_INSERT_ID()
</selectKey>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
public void test6() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setBirthday(new Date());
user.setSex("男");
user.setUsername("秦贺");
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId());
sqlSession.commit();
System.out.println(user.getId());
}
3、另一种方式返回id 的添加
<insert id="insertUser" parameterType="com.itcast.domain.User"
useGeneratedKeys="true"
keyProperty="id"
>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
java程序和上边一样
4、oracle的做法
<insert id="insertUser" parameterType="com.itcast.domain.User" >
<selectKey keyProperty="id" order="BEFORE" resultType="int" >
SELECT 自定义序列.nextval from dual
</selectKey>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
oracle自增主键是序列化自增类型,需要before
5、主键生成策略改为uuid
三、update
根据id修改用户
<update id="updateUserByID" parameterType="com.itcast.domain.User" >
update user set username=#{username},
birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
public void test7() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(28);
user.setBirthday(new Date());
user.setSex("女");
user.setUsername("秦贺");
user.setAddress("23");
sqlSession.insert("test.updateUserByID", user);
sqlSession.commit();
}
四、delete
根据id删除记录
<delete id="deleteUserByID" parameterType="int" >
delete from user where id=#{id}
</delete>
public void test8() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUserByID",28);
sqlSession.commit();
}
一、原始Dao开发方式:
1、dao实现类
public class UserDaoImpl implements UserDao{
private SqlSessionFactory sqlSessionFactory=null;
public UserDaoImpl(SqlSessionFactory sessionFactory) {
this.sqlSessionFactory=sessionFactory;
}
@Override
public User findUserWithId(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithId",id);
return user;
}
@Override
public List<User> findUserByUsername(String username) {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserWithUsername",username);
return list;
}
}
2、测试类
public void fun1() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
UserDao userDao= new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserWithId(27);
List<User> list = userDao.findUserByUsername("张");
System.out.println(user);
System.out.println(list);
}
原始Dao开发中存在以下问题:
uDao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
u调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不便于维护。
二、接口代理开发
1、第一种接口代理开发
引入映射文件方式和普通方式是一样的
<mapper resource="sqlMap/UserMapper.xml" />
第一种引入映射文件文件方法:
1,传统开发模式
2,接口代理开发模式
第一种方式开发接口代理开发:
引入映射文件方式:mapper resource="mapper.xml"
开发规范:
1,映射文件namespace必须是接口全类路径名 namespace="com.itcast.dao.UserDao"
2,映射文件id必须和接口方法名相同
3,映射文件接受参数类型必须和传递参数类型一致
4,映射文件返回值类型必须和接口返回值类型一致
按照上述规范修改配置文件
测试:
public void fun1() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findUserWithId(27);
System.out.println(user);
}
2、第二种接口代理开发
第二种引入映射文件文件方法:(通过接口代理方法进行引入)
1,只能使用接口代理开发模式
开发规范:
1,映射文件namespace必须是接口全类路径名 namespace=""com.itheima.dao.UserMapper"
2,映射文件id必须和接口方法名相同
3,映射文件和接口必须在同一个目录,且名称相同
4,映射文件接受参数类型必须和传递参数类型一致
5,映射文件返回值类型必须和接口返回值类型一致
扫描单个单个接口:
<mapper class="com.itheima.dao.UserMapper"/>
把接口UserDao文件名改成UserMapper,把namespace改成com.itcast.dao.UserMapper,把UserMapper.xml和UserMapper.java放在同一个目录
目录结构如下:
3、第三种接口代理开发
第三种引入映射文件方式:批量扫描接口,使用接口代理开发
只能使用接口代理开发模式
必须满足上面5条开发规范
<mappers>
<package name="com.itheima.dao"/>
</mappers>
mybatis和hibernate优缺点
1、hibernate优点
完全面向对象的开发方式:HQL
数据库迁移的时候比较方便(只需要修改方言即可)
2、hibernate缺点
HQL翻译成sql语句才能执行,降低了框架的执行效率
HQL、SQL写在了代码当中,不利于程序维护
HQL翻译后生成的sql语句太难看,不利于调试程序
对表关系维护太复杂,不利于开发优化
HQL语句不能开发复杂业务
适应场景:适合需求变化较少的项目,ERP、OA。。。
3、mybatis的优点
jdbc演化mybatis框架流程:5条优化
使用原生sql语句,直接执行,效率高
适合开发项目:需求变化快的项目,互联网
一、jdbc开发程序:
1.package cn.itcast.jdbc.test;
2.
3.import java.sql.Connection;
4.import java.sql.DriverManager;
5.import java.sql.PreparedStatement;
6.import java.sql.ResultSet;
7.import java.sql.SQLException;
8.
9.public class JDBCTest {
10.
11. public static void main(String[] args) {
12. Connection connection = null;
13. PreparedStatement preparedStatement = null;
14. ResultSet resultSet = null;
15. try {
16. //加载数据库驱动
17. Class.forName("com.mysql.jdbc.Driver");
18.
19. //通过驱动管理类获取数据库链接
20. connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis01?characterEncoding=utf-8", "root", "admin");
21. //定义sql语句 ?表示占位符
22. String sql = "select * from user where username = ?";
23. //获取预处理statement
24. preparedStatement = connection.prepareStatement(sql);
25. //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
26. preparedStatement.setString(1, "王五");
27. //向数据库发出sql执行查询,查询出结果集
28. resultSet = preparedStatement.executeQuery();
29. //遍历查询结果集
30. while(resultSet.next()){
31. System.out.println(resultSet.getString("id")+" "+resultSet.getString("username"));
32. }
33. } catch (Exception e) {
34. e.printStackTrace();
35. }finally{
36. //释放资源
37. if(resultSet!=null){
38. try {
39. resultSet.close();
40. } catch (SQLException e) {
41. // TODO Auto-generated catch block
42. e.printStackTrace();
43. }
44. }
45. if(preparedStatement!=null){
46. try {
47. preparedStatement.close();
48. } catch (SQLException e) {
49. // TODO Auto-generated catch block
50. e.printStackTrace();
51. }
52. }
53. if(connection!=null){
54. try {
55. connection.close();
56. } catch (SQLException e) {
57. // TODO Auto-generated catch block
58. e.printStackTrace();
59. }
60. }
61.
62. }
63.
64. }
65.
66.
67.}
二、Mybatis优化:
1、第一步优化:连接获取和释放
问题描述:
数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能。
解决问题:
数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题。通过连接池就可以反复利用已经建立的连接去访问数据库了。减少连接的开启和关闭的时间。
问题描述:
但是现在连接池多种多样,可能存在变化,有可能采用DBCP的连接池,也有可能采用容器本身的JNDI数据库连接池。
解决问题:
我们可以通过DataSource进行隔离解耦,我们统一从DataSource里面获取数据库连接,DataSource具体由DBCP实现还是由容器的JNDI实现都可以,所以我们将DataSource的具体实现通过让用户配置来应对变化。
2、第二步优化:SQL存取
问题描述:
我们使用JDBC进行操作数据库时,SQL语句基本都散落在各个JAVA类中,这样有三个不足之处:
第一,可读性很差,不利于维护以及做性能调优。
第二,改动Java代码需要重新编译、打包部署。
第三,不利于取出SQL在数据库客户端执行(取出后还得删掉中间的Java代码,编写好的SQL语句写好后还得通过+号在Java进行拼凑)。
解决问题:
我们可以考虑不把SQL语句写到Java代码中,那么把SQL语句放到哪里呢?首先需要有一个统一存放的地方,我们可以将这些SQL语句统一集中放到配置文件或者数据库里面(以key-value的格式存放)。然后通过SQL语句的key值去获取对应的SQL语句。
既然我们将SQL语句都统一放在配置文件或者数据库中,那么这里就涉及一个SQL语句的加载问题。
3、第三步优化:传入参数映射和动态SQL
问题描述:
很多情况下,我们都可以通过在SQL语句中设置占位符来达到使用传入参数的目的,这种方式本身就有一定局限性,它是按照一定顺序传入参数的,要与占位符一一匹配。但是,如果我们传入的参数是不确定的(比如列表查询,根据用户填写的查询条件不同,传入查询的参数也是不同的,有时是一个参数、有时可能是三个参数),那么我们就得在后台代码中自己根据请求的传入参数去拼凑相应的SQL语句,这样的话还是避免不了在Java代码里面写SQL语句的命运。既然我们已经把SQL语句统一存放在配置文件或者数据库中了,怎么做到能够根据前台传入参数的不同,动态生成对应的SQL语句呢?
解决问题:
Mybatis自动将Java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型
4、第四步优化:结果映射和结果缓存
问题描述:
执行SQL语句、获取执行结果、对执行结果进行转换处理、释放相关资源是一整套下来的。假如是执行查询语句,那么执行SQL语句后,返回的是一个ResultSet结果集,这个时候我们就需要将ResultSet对象的数据取出来,不然等到释放资源时就取不到这些结果信息了。我们从前面的优化来看,以及将获取连接、设置传入参数、执行SQL语句、释放资源这些都封装起来了,只剩下结果处理这块还没有进行封装,如果能封装起来,每个数据库操作都不用自己写那么一大堆Java代码,直接调用一个封装的方法就可以搞定了。
解决问题:
我们分析一下,一般对执行结果的有哪些处理,有可能将结果不做任何处理就直接返回,也有可能将结果转换成一个JavaBean对象返回、一个Map返回、一个List返回等等,结果处理可能是多种多样的。从这里看,我们必须告诉SQL处理器两点:第一,需要返回什么类型的对象;第二,需要返回的对象的数据结构怎么跟执行的结果映射,这样才能将具体的值copy到对应的数据结构上。
接下来,我们可以进而考虑对SQL执行结果的缓存来提升性能。缓存数据都是key-value的格式,那么这个key怎么来呢?怎么保证唯一呢?即使同一条SQL语句几次访问的过程中由于传入参数的不同,得到的执行SQL语句也是不同的。那么缓存起来的时候是多对。但是SQL语句和传入参数两部分合起来可以作为数据缓存的key值。
5、第五步优化:解决重复SQL语句问题
问题描述:
由于我们将所有SQL语句都放到配置文件中,这个时候会遇到一个SQL重复的问题,几个功能的SQL语句其实都差不多,有些可能是SELECT后面那段不同、有些可能是WHERE语句不同。有时候表结构改了,那么我们就需要改多个地方,不利于维护。
解决问题:
当我们的代码程序出现重复代码时怎么办?将重复的代码抽离出来成为独立的一个类,然后在各个需要使用的地方进行引用。对于SQL重复的问题,我们也可以采用这种方式,通过将SQL片段模块化,将重复的SQL片段独立成一个SQL块,然后在各个SQL语句引用重复的SQL块,这样需要修改时只需要修改一处即可。
6、优化总结:
(1) 使用数据库连接池对连接进行管理
(2) SQL语句统一存放到配置文件
(3) SQL语句变量和传入参数的映射以及动态SQL
(4) 动态SQL语句的处理
(5) 对数据库操作结果的映射和结果缓存
三、Mybatis简介:
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回
Mybatis是apache旗下的顶级一个项目,mybatis是对jdbc的封装的一个持久层框架
mybatis配置
1、SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
2、mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
3、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
4、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
5、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
6、Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
7、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
8、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
四、入门程序
1、需求:
1、根据用户主见进行查询用户信息
2、根据用户名称模糊查询用户信息
3、添加用户
4、删除用户
5、更新用户
2、导入jar包
3、创建sqlMapConfig.xml
a、基本配置
<?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:配置mybatis基础支持层环境开始标签 -->
<configuration>
<!-- environments:mybatis连接数据库环境,可以配置多个缓存,开发环境,测试缓存 -->
<environments default="development">
<!-- environment:开发环境 -->
<environment id="development">
<!-- 事物,使用jdbc事物 -->
<transactionManager type="JDBC" />
<!-- 数据源 连接池POOLED -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.66.66:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="admin" />
</dataSource>
</environment>
</environments>
<!-- 引入第三方映射文件,类似hibernate中hbm -->
<mappers>
<mapper resource="sqlMap/UserMapper.xml" />
</mappers>
</configuration>
b、可以配置外部引用配置文件
<configuration>
<properties resource="jdbc.properties" ></properties>
<!-- environments:mybatis连接数据库环境,可以配置多个缓存,开发环境,测试缓存 -->
<environments default="development">
<!-- environment:开发环境 -->
<environment id="development">
<!-- 事物,使用jdbc事物 -->
<transactionManager type="JDBC" />
<!-- 数据源 连接池POOLED -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 引入第三方映射文件,类似hibernate中hbm -->
<mappers>
<mapper resource="sqlMap/UserMapper.xml" />
</mappers>
</configuration>
c、mybatis别名定义
定义单个别名:
一般都是给pojo对象定义别名,且每个对象定义一个别名
<!-- 给每个pojo对象定义一个别名 -->
<typeAliases>
<typeAlias type="com.itcast.domain.User" alias="user" />
<!--
type:指定定义别名的类型
alias:指定别名的名称
-->
</typeAliases>
在properties和environments标签之间
批量扫描方法定义别名
package:扫描定义别名所在的包路径即可, 此包路径下的所有类自动被定义别名,别名名称:类名,大小写随意
<typeAliases>
<package name="com.itcast.domain"/>
</typeAliases>
d、其他属性设置值:
properties 引入外部资源文件
settings?, // 设置缓存,查询缓存,二级缓存
typeAliases?, //定义别名
typeHandlers?, //把数据库类型(Varchar)转换java类型(String) TypeHandlerRegistry
objectFactory?,//对象工厂,创建返回值对象
objectWrapperFactory?,
reflectorFactory?,
plugins?, //配置插件(分页插件)
environments?,(配置环境,数据源,事物)
databaseIdProvider?,
mappers?) //引入外部映射mapper文件
5、创建实体类(javabean)
6、创建映射配置文件UserMapper.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:映射文件开始标签,标签中封装所有sql语句
namespace:
1,命名空间,唯一标识一个映射文件,名称自定义
2,如果是接口代理开发,namespace有特殊含义,不能随便定义
-->
<mapper namespace="test">
<!--
需求:查询所有用户
select:用来封装select查询语句标签
id:sql语句唯一标识
resultType:指定返回值类型,无论返回是pojo,集合类型,都返回的是pojo对象类型
-->
<select id="findAllUser" resultType="com.itheima.domain.User">
select * from user
</select>
<!--
需求:根据id查询用户
select:用来封装select查询语句标签,所有查询语句都必须使用select标签封装
id:sql语句唯一标识
parameterType:指定参数类型,通常使用别名定义 (int就是别名)
resultType:指定返回值类型
#{}:占位符,mybatis框架默认自动加上?,#{}自动获取传递参数
特点:如果传递参数是基本类型,#{}括号里面可以是任意值
-->
<select id="findUserWithId" parameterType="int" resultType="com.itheima.domain.User">
select * from user where id=#{张三丰}
</select>
<!--
需求:根据用户名模糊查询用户(查询姓张的用户)
select:用来封装select查询语句标签,所有查询语句都必须使用select标签封装
id:sql语句唯一标识
parameterType:指定参数类型,通常使用别名定义 (string就是别名)
resultType:指定返回值类型
#{}:mybatis框架自动提供特殊模式进行模糊查询语句组装:"%"#{username}"%"=='%张%'
特点:
1,"%"#{username}"%" 是mybatis提供特殊写法,自动构造模糊查询
2,如果传递数据是字符串类型,#{username}获取的数据自动加上双引号 "张"
3,如果传递参数是基本类型,#{}可以是任意值
4,如果传递参数是pojo对象,#{}使用ognl(对象导航语言)表达式获取参数
ognl语法:属性.属性.属性...
${}:sql语句拼接:'%${value}%'
1,${}获取数据无论是什么类型数据,原样获取,不加任何修饰
2,${}如果获取是基本类型(int,long,string)的数据,${}里面必须是value
3,如果传递参数是pojo对象,#{}使用ognl(对象导航语言)表达式获取参数
4,${}就是sql拼接,会有sql语句注入风险,一般使用#
-->
<select id="findUserWithUsername" parameterType="string" resultType="com.itheima.domain.User">
select * from user where username LIKE "%"#{username}"%"
</select>
<!--
需求:添加用户
insert:用来封装insert插入语句标签
id:sql语句唯一标识
parameterType:指定传递参数类型
传递参数:pojo类型,获取参数时候使用ognl表达式获取
-->
<insert id="insertUser" parameterType="com.itheima.domain.User" useGeneratedKeys="true" keyProperty="id">
<!--
selectKey:用来返回主键标识标签
keyProperty:标识返回注解对应javabean设置属性
order:返回策略,id是在sql语句执行之后生成,因此使用AFTER
resultType:指定返回值类型
-->
<!-- <selectKey keyProperty="id" order="AFTER" resultType="int">
SELECT LAST_INSERT_ID()
</selectKey> -->
insert into user values(#{id},
#{username},
#{birthday},
#{sex},
#{address})
</insert>
<!--
需求:根据id修改用户
-->
<update id="updateUserByID" parameterType="com.itheima.domain.User">
update user set username=#{username},
birthday=#{birthday},
sex=#{sex},
address=#{address}
where id=#{id}
</update>
<!-- 需求:根据id删除用户 -->
<delete id="deleteUserByID" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>
7、测试类
package com.itheima.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
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.Before;
import org.junit.Test;
import com.itheima.domain.User;
public class MybatisTest {
SqlSessionFactory sqlSessionFactory=null;
/**
* 需求:抽取公共代码
* @throws Exception
*/
@Before
public void beforeConfig() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取sqlSessionFacory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 需求:查询所有用户
* @throws Exception
*/
@Test
public void test01() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取sqlSessionFacory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从工厂中获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用sqlSession接口方法
//找到Mapped映射文件中sql语句执行
//如何唯一定位一条sql语句-----namespace.id
List<User> list = sqlSession.selectList("test.findAllUser");
System.out.println(list);
}
/**
* 需求:根据id查询用户
* 参数:int id
* 返回值:User
* @throws Exception
*/
@Test
public void test02() throws Exception{
//读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//通过工厂构建类,获取工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从工厂中获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用接口查询方法
//返回值只有一个对象,selectOne
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
User user = sqlSession.selectOne("test.findUserWithId", 30);
System.out.println(user);
}
/**
* 需求:根据用户名进行模糊查询
*/
@Test
public void test03(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
List<User> list = sqlSession.selectList("test.findUserWithUsername", "张");
System.out.println(list);
}
/**
* 需求:保存用户数据
*/
@Test
public void test04(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建user对象,封装保存数据
User user = new User();
user.setUsername("赵敏3");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("大都3");
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.insert("test.insertUser", user);
//返回保存user对象主键,把主键值设置order对象外键中,订单和user关系,必须手动维护
System.out.println("user对象主键:"+user.getId());
//提交事物
sqlSession.commit();
System.out.println("user对象主键:"+user.getId());
}
/**
* 需求:保存用户数据
*/
@Test
public void test05(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建user对象,封装保存数据
User user = new User();
user.setId(34);
user.setUsername("周芷若");
user.setSex("女");
user.setBirthday(new Date());
user.setAddress("峨眉山");
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.update("test.updateUserByID", user);
//提交事物
sqlSession.commit();
}
/**
* 需求:保存用户数据
*/
@Test
public void test06(){
//从工厂获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//调用方法
//参数1:唯一定位一条执行sql语句(命名空间+sql语句id) --namespace.id
//参数2:查询条件参数
sqlSession.delete("test.deleteUserByID", 33);
//提交事物
sqlSession.commit();
}
}
#{}:mybatis框架自动提供特殊模式进行模糊查询语句组装:"%"#{username}"%"=='%张%'
特点:
1,"%"#{}"%" 是mybatis提供特殊写法,自动构造模糊查询
2,如果传递数据是字符串类型,#{}获取的数据自动加上双引号 "张"
3,如果传递参数是基本类型,#{}括号里面的内容可以是任意值
4,如果传递参数是pojo对象,#{}括号里面的内容使用ognl(对象导航语言)表达式获取参数
ognl语法:属性.属性.属性...
${}:sql语句拼接:'%${value}%'
1,${}获取数据无论是什么类型数据,原样获取,不加任何修饰
2,${}如果获取是基本类型(int,long,string)的数据,${}括号里面必须是value
3,如果传递参数是pojo对象,${}括号里面使用ognl(对象导航语言)表达式获取参数
4,${}就是sql拼接,会有sql语句注入风险,一般使用#
一、select:
1、查询所有用户
<select id="findAllUser" resultType="com.itcast.domain.User" >
select * from user
</select>
public void test01() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findAllUser");
System.out.println(list);
}
2、根据id查询用户
此时#{}括号里面内容可以写任何字符,但是如果要用${}里面只能写value
<select id="findUserWithId" parameterType="int" resultType="com.itcast.domain.User" >
select * from user where id=#{id}
</select>
public void test2() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithId",26);
System.out.println(user);
}
因为id是int型的,所以不能加双引号
3、根据username准确查询用户
<select id="findUserWithUsername" parameterType="string" resultType="com.itcast.domain.User" >
select * from user where username="${value}"
</select> -->
此时username=的参数必须要加双引号,所以${}两边加了双引号
而如果是#{},两边就不用加双引号的,因为已经自动加了
public void test3() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithUsername","王五");
System.err.println(user);
}
4、根据username模糊查询
<select id="findUserWithUsername" parameterType="string" resultType="com.itcast.domain.User" >
select * from user where username like "%${value}%"
</select>
public void test4() throws Exception{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserWithUsername","王五");
System.out.println(list);
}
二、insert
1、普通没有返回值添加用户
<insert id="insertUser" parameterType="com.itcast.domain.User" >
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
public void test5() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setBirthday(new Date());
user.setSex("男");
user.setUsername("秦贺");
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
}
2、返回id的添加
<insert id="insertUser" parameterType="com.itcast.domain.User" >
<selectKey keyProperty="id" order="AFTER" resultType="int" >
SELECT LAST_INSERT_ID()
</selectKey>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
public void test6() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setBirthday(new Date());
user.setSex("男");
user.setUsername("秦贺");
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId());
sqlSession.commit();
System.out.println(user.getId());
}
3、另一种方式返回id 的添加
<insert id="insertUser" parameterType="com.itcast.domain.User"
useGeneratedKeys="true"
keyProperty="id"
>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
java程序和上边一样
4、oracle的做法
<insert id="insertUser" parameterType="com.itcast.domain.User" >
<selectKey keyProperty="id" order="BEFORE" resultType="int" >
SELECT 自定义序列.nextval from dual
</selectKey>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
oracle自增主键是序列化自增类型,需要before
5、主键生成策略改为uuid
三、update
根据id修改用户
<update id="updateUserByID" parameterType="com.itcast.domain.User" >
update user set username=#{username},
birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>
public void test7() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setId(28);
user.setBirthday(new Date());
user.setSex("女");
user.setUsername("秦贺");
user.setAddress("23");
sqlSession.insert("test.updateUserByID", user);
sqlSession.commit();
}
四、delete
根据id删除记录
<delete id="deleteUserByID" parameterType="int" >
delete from user where id=#{id}
</delete>
public void test8() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUserByID",28);
sqlSession.commit();
}
一、原始Dao开发方式:
1、dao实现类
public class UserDaoImpl implements UserDao{
private SqlSessionFactory sqlSessionFactory=null;
public UserDaoImpl(SqlSessionFactory sessionFactory) {
this.sqlSessionFactory=sessionFactory;
}
@Override
public User findUserWithId(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserWithId",id);
return user;
}
@Override
public List<User> findUserByUsername(String username) {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserWithUsername",username);
return list;
}
}
2、测试类
public void fun1() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
UserDao userDao= new UserDaoImpl(sqlSessionFactory);
User user = userDao.findUserWithId(27);
List<User> list = userDao.findUserByUsername("张");
System.out.println(user);
System.out.println(list);
}
原始Dao开发中存在以下问题:
uDao方法体存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
u调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不便于维护。
二、接口代理开发
1、第一种接口代理开发
引入映射文件方式和普通方式是一样的
<mapper resource="sqlMap/UserMapper.xml" />
第一种引入映射文件文件方法:
1,传统开发模式
2,接口代理开发模式
第一种方式开发接口代理开发:
引入映射文件方式:mapper resource="mapper.xml"
开发规范:
1,映射文件namespace必须是接口全类路径名 namespace="com.itcast.dao.UserDao"
2,映射文件id必须和接口方法名相同
3,映射文件接受参数类型必须和传递参数类型一致
4,映射文件返回值类型必须和接口返回值类型一致
按照上述规范修改配置文件
测试:
public void fun1() throws IOException{
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findUserWithId(27);
System.out.println(user);
}
2、第二种接口代理开发
第二种引入映射文件文件方法:(通过接口代理方法进行引入)
1,只能使用接口代理开发模式
开发规范:
1,映射文件namespace必须是接口全类路径名 namespace=""com.itheima.dao.UserMapper"
2,映射文件id必须和接口方法名相同
3,映射文件和接口必须在同一个目录,且名称相同
4,映射文件接受参数类型必须和传递参数类型一致
5,映射文件返回值类型必须和接口返回值类型一致
扫描单个单个接口:
<mapper class="com.itheima.dao.UserMapper"/>
把接口UserDao文件名改成UserMapper,把namespace改成com.itcast.dao.UserMapper,把UserMapper.xml和UserMapper.java放在同一个目录
目录结构如下:
3、第三种接口代理开发
第三种引入映射文件方式:批量扫描接口,使用接口代理开发
只能使用接口代理开发模式
必须满足上面5条开发规范
<mappers>
<package name="com.itheima.dao"/>
</mappers>
mybatis和hibernate优缺点
1、hibernate优点
完全面向对象的开发方式:HQL
数据库迁移的时候比较方便(只需要修改方言即可)
2、hibernate缺点
HQL翻译成sql语句才能执行,降低了框架的执行效率
HQL、SQL写在了代码当中,不利于程序维护
HQL翻译后生成的sql语句太难看,不利于调试程序
对表关系维护太复杂,不利于开发优化
HQL语句不能开发复杂业务
适应场景:适合需求变化较少的项目,ERP、OA。。。
3、mybatis的优点
jdbc演化mybatis框架流程:5条优化
使用原生sql语句,直接执行,效率高
适合开发项目:需求变化快的项目,互联网