前几天学习了mybatis,今天来复习一下它的内容。
mybatis是一个基于Java的持久层框架,那就涉及到数据库的操作。首先来提出第一个问题:java有jdbc连接数据库,我们为什么还要使用框架呢?要回答这个问题,首先来看一下jdbc是怎样编程的。
1 private static String sql = "SELECT * FROM USER WHERE username = ?"; 2 3 public static void main(String[] args) throws SQLException { 4 5 //数据库连接 6 Connection connection = null; 7 //预编译statement 8 //好处:防止 sql注入,提高数据的性能 9 PreparedStatement preparedStatement = null; 10 //结果集 11 ResultSet resultSet = null; 12 13 try { 14 15 //加载数据库驱动 16 Class.forName("com.mysql.jdbc.Driver"); 17 18 //连接数据库 19 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "mysql"); 20 21 //构造preparedStatement 22 preparedStatement = connection.prepareStatement(sql); 23 //向preparedStatement中占位符的位置设置参数 24 preparedStatement.setString(1, "张三"); 25 26 //发起数据库查询请求,得到结果集 27 resultSet = preparedStatement.executeQuery(); 28 //遍历查询结果 29 30 while(resultSet.next()){ 31 int id = resultSet.getInt("id"); 32 String username = resultSet.getString("username"); 33 Date birthday = resultSet.getDate("birthday"); 34 System.out.println(id+" "+username+" "+birthday); 35 } 36 37 38 } catch (Exception e) { 39 e.printStackTrace(); 40 }finally{ 41 //释放资源 42 if(resultSet!=null){ 43 resultSet.close(); 44 } 45 if(preparedStatement!=null){ 46 preparedStatement.close(); 47 } 48 if(connection!=null){ 49 connection.close(); 50 } 51 } 52 53 54 } 55 56 }
从上面代码总结一下jdbc编程中的一些问题:
1、 将sql语句硬编码到java代码中,如果修改sql语句,需要修改java代码,重新编译。系统可维护性不高。
设想如何解决?
能否将sql单独 配置在配置文件中。
2、 数据库连接频繁开启和释放,对数据库的资源是一种浪费。
设想如何解决?
使用数据库连接池管理数据库连接。
3、 向preparedStatement中占位符的位置设置参数时,存在硬编码(占位符的位置,设置的变量值)
设想如何解决?
能否也通过配置的方式,配置设置的参数,自动进行设置参数。
4、 解析结果集时存在硬编码(表的字段名、字段的类型)
设想如何解决?
能否将查询结果集映射成java对象。
先来看看Mybatis的流程图:
以一个具体的案例来说明,先来看看表:
这张表已经被我使用过多次,插入一些数据了。
接下来,第一件事,导包。(会maven的童鞋自己去百度下pom.xml的配置,这个小案例我这里就不用maven了)
项目的整体目录是这个样子的:
先来讲config这个目录下:
全局配置文件:SqlMapConfig.xml文件内容:
数据库运行环境(和spring整合则去掉这部分)
Mapper映射文件。
实现延迟加载,打开二级缓存。
定义别名。
代码如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <!-- 加载数据库连接参数配置文件 --> 7 <properties resource="db.properties" /> 8 9 <!-- 全局配置参数 --> 10 11 <settings> 12 <!-- 实现延迟加载 --> 13 <setting name="lazyLoadingEnabled" value="true" /> 14 <setting name="aggressiveLazyLoading" value="false" /> 15 16 <!-- 打开二级缓存开关 --> 17 <setting name="cacheEnabled" value="true"/> 18 </settings> 19 20 21 22 <!-- 定义别名 --> 23 <typeAliases> 24 <!-- 单个别名定义 type:pojo的路径 alias:别名的名称 --> 25 <!-- <typeAlias type="com.mybatis.po.User" alias="user"/> --> 26 <!-- 批量别名定义 name:指定包名,将包下边的所有pojo定义别名 ,别名为类名(首字母大写或小写都行) --> 27 <package name="com.mybatis.po" /> 28 </typeAliases> 29 30 <!-- 和spring整合后 environments配置将废除 --> 31 <environments default="development"> 32 <environment id="development"> 33 <transactionManager type="JDBC" /> 34 <dataSource type="POOLED"> 35 <property name="driver" value="${jdbc.driver}" /> 36 <property name="url" value="${jdbc.url}" /> 37 <property name="username" value="${jdbc.username}" /> 38 <property name="password" value="${jdbc.password}" /> 39 </dataSource> 40 </environment> 41 </environments> 42 43 <!-- 配置mapper映射文件 --> 44 <mappers> 45 <!-- resource方式 在UserMapper.xml,定义namespace为mapper接口的地址,映射文件通过namespace找到对应的mapper接口文件 --> 46 <!-- <mapper resource="sqlmap/UserMapper.xml" /> --> 47 <!-- class方式 class:指定 mapper接口的地址 遵循规则:将mapper.xml和mapper.java文件放在一个目录 48 且文件名相同 --> 49 <!-- <mapper class="com.mybatis.mapper.UserMapper"/> --> 50 51 52 <!--批量mapper扫描 遵循规则:将mapper.xml和mapper.java文件放在一个目录 且文件名相同 --> 53 <package name="com.mybatis.mapper" /> 54 55 56 </mappers> 57 </configuration>
db.properties
1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/test 3 jdbc.username=root 4 jdbc.password=202011
log4j.properties:使用debug。
1 # Global logging configuration 2 log4j.rootLogger=DEBUG, stdout 3 # Console output... 4 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
po类:user.java 要存入二级缓存,所以序列化了。
1 package com.mybatis.po; 2 3 import java.io.Serializable; 4 5 14 public class User implements Serializable{ 15 16 /** 17 * 18 */ 19 private static final long serialVersionUID = 1L; 20 21 private int id; 22 private String username;// 用户姓名 23 24 private String age; 25 26 private String sex;// 性别 27 private String address;// 地址 28 private String detail;// 详细信息 29 public User() { 30 31 32 } 33 public User(String username) { 34 super(); 35 this.username = username; 36 } 37 38 public String getAge() { 39 return age; 40 } 41 public void setAge(String age) { 42 this.age = age; 43 } 44 public int getId() { 45 return id; 46 } 47 public void setId(int id) { 48 this.id = id; 49 } 50 public String getUsername() { 51 return username; 52 } 53 public void setUsername(String username) { 54 this.username = username; 55 } 56 public String getSex() { 57 return sex; 58 } 59 public void setSex(String sex) { 60 this.sex = sex; 61 } 62 63 public String getAddress() { 64 return address; 65 } 66 public void setAddress(String address) { 67 this.address = address; 68 } 69 public String getDetail() { 70 return detail; 71 } 72 public void setDetail(String detail) { 73 this.detail = detail; 74 } 75 76 @Override 77 public String toString() { 78 return "User [id=" + id + ", username=" + username + ", age=" + age 79 + ", sex=" + sex + ", address=" + address + ", detail=" 80 + detail + "]"; 81 } 82 83 84 85 86 }
配置mybatis的mapper映射文件:
userMapper.xml:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <!-- namespace命名空间特殊作用: 如果使用mapper动态代理方法,这里就需要配置mapper接口地址--> 6 7 <mapper namespace="com.mybatis.mapper.UserMapper"> 8 <!-- 根据用户id查询一条记录(返回单条记录) --> 9 <!-- 10 select标签表示sql查询,内容会封装到Mapped Statement中。 11 可以将这个select标签称为一个Statement 12 id:Statement的id,用于标识select中定义的 sql,id是在同一个命名空间中不允许重复 13 #{}:表示一个占位符,避免sql注入 14 parameterType:表示输入参数的类型 15 resultType:表示输出 结果集单条记录映射的java对象类型,select查询的字段名和resultType中属性名一致,才能映射成功。 16 #{value}:value表示parameter输入参数的变量,如果输入参数是简单类型,使用#{}占位符,变量名可以使用value或其它的名称 17 18 --> 19 <select id="findUserByName" parameterType="String" resultType="User" useCache="false"> 20 21 SELECT * FROM USER WHERE username = #{username} 22 23 </select> 24 25 26 <select id="findUserById" parameterType="int" resultType="User"> 27 28 SELECT * FROM USER WHERE id= #{id} 29 30 </select> 31 32 33 <!-- 查询用户列表(返回list集合) --> 34 <!-- 35 不管结果集查询一条还是多条,resultType指定结果集单条记录映射的java对象类型 36 ${}:表示sql拼接,相当于sql字符串拼接,无法避免sql注入 37 ${value}:value表示parameter输入参数的变量,如果输入参数是简单类型,使用${}拼接符,变量名必须使用value 38 ${value}直接 将value获取到拼接在sql中,value值不加任何修饰 39 --> 40 <select id="findUserList" parameterType="java.lang.String" resultType="User" > 41 SELECT * FROM USER WHERE username like ‘%${value}%‘ 42 </select> 43 44 45 <!-- 添加用户 46 parameterType:如果parameterType指定 是pojo,在#{}中指定 pojo的属性名获取该pojo的属性值 47 --> 48 <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> 49 50 <!-- 51 useGeneratedKeys设置true表明要mybatis获取由数据库自动生成的主键 52 keyProperty:将主键设置到pojo中哪个属性中 53 order:selectKey中sql执行的时机 54 resultType:selectKey中sql执行的结果类型 55 LAST_INSERT_ID:是insert后获取自增主键值 56 --> 57 <!-- 58 <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> 59 select LAST_INSERT_ID() 60 </selectKey> 61 --> 62 insert into user(username,sex,address,detail) 63 values(#{username},#{sex},#{address},#{detail}) 64 </insert> 65 66 <!-- 根据主键删除用户 --> 67 68 <delete id="deleteUser" parameterType="java.lang.String"> 69 delete from user where username =#{username} 70 </delete> 71 72 <!-- 根据主键用户更新 73 更新传入输入参数见容:id和更新的信息 74 --> 75 <update id="updateUser" parameterType="User" > 76 update user set username=#{username},sex=#{sex},address=#{address},detail=#{detail} where id=#{id} 77 </update> 78 79 80 <cache/> 81 </mapper>
userMapper.java:
1 package com.mybatis.mapper; 2 3 import java.util.List; 4 5 import com.mybatis.po.User; 6 7 8 17 public interface UserMapper { 18 19 //根据用户id查询用户信息 20 public User findUserById(int id) throws Exception; 21 22 //根据用户名字查询信息 23 public User findUserByName(String username) throws Exception; 24 25 //查询用户列表 26 public List<User> findUserList(String username)throws Exception; 27 28 public void deleteUser(String username)throws Exception; 29 30 public int insertUser(User user)throws Exception; 31 32 public void updateUser(User user)throws Exception; 33 34 }
Test.java:
1 package com.mybatis.mapper; 2 3 import java.io.InputStream; 4 import java.util.List; 5 6 import org.apache.ibatis.io.Resources; 7 import org.apache.ibatis.session.SqlSession; 8 import org.apache.ibatis.session.SqlSessionFactory; 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 10 import org.junit.Before; 11 import org.junit.Test; 12 13 import com.mybatis.po.User; 14 15 public class UserMapperTest { 16 17 // 会话工厂 18 private SqlSessionFactory sqlSessionFactory; 19 20 @Before 21 public void setUp() throws Exception { 22 // 加载配置文件 23 String resource = "SqlMapConfig.xml"; 24 InputStream inputStream = Resources.getResourceAsStream(resource); 25 26 // 根据mytais的配置创建SqlSessionFactory 27 28 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 29 30 //sqlSessionFactory.getConfiguration().addMapper(UserMapper.class); 31 } 32 33 @Test 34 public void testFindUserByName() throws Exception { 35 SqlSession sqlSession = sqlSessionFactory.openSession(); 36 //通过sqlSession创建代理对象(接口的实现类对象) 37 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 38 User user= userMapper.findUserByName("张小三"); 39 System.out.println(user); 40 } 41 42 @Test 43 public void testFindUserList() throws Exception { 44 SqlSession sqlSession = sqlSessionFactory.openSession(); 45 //通过sqlSession创建代理对象(接口的实现类对象) 46 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 47 List<User> list = userMapper.findUserList("d"); 48 for(User x:list){ 49 System.out.println(x.getUsername()); 50 } 51 52 53 } 54 55 /* 56 由于我的这个user表被其他表的外键连接,所以这里暂不进行删除操作。 57 @Test 58 public void deleteUser() throws Exception { 59 SqlSession sqlSession = sqlSessionFactory.openSession(); 60 //通过sqlSession创建代理对象(接口的实现类对象) 61 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 62 userMapper.deleteUser("duyu"); 63 sqlSession.commit(); 64 sqlSession.close(); 65 66 67 } 68 */ 69 70 71 @Test 72 public void insertUser() throws Exception { 73 SqlSession sqlSession = sqlSessionFactory.openSession(); 74 //通过sqlSession创建代理对象(接口的实现类对象) 75 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 76 User user=new User("2017-3-12"); 77 user.setAddress("四川"); 78 int z=userMapper.insertUser(user); 79 sqlSession.commit(); 80 sqlSession.close(); 81 System.out.println(z); 82 83 } 84 85 @Test 86 public void updateUser() throws Exception { 87 SqlSession sqlSession = sqlSessionFactory.openSession(); 88 //通过sqlSession创建代理对象(接口的实现类对象) 89 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 90 User user=userMapper.findUserByName("duu"); 91 user.setAddress("北京"); 92 user.setUsername("Eminem"); 93 userMapper.updateUser(user); 94 sqlSession.commit(); 95 sqlSession.close(); 96 97 98 } 99 100 }
执行完操作后表如下:
mybatis对jdbc编程问题的解决:
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用连接池管理数据库链接。
2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
4、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。