- 创建resourcesjdbc.properties
db.username=root db.password=root db.jdbcUrl=jdbc:mysql:///mybatisdb?useSSL=false&serverTimezone=UTC db.driverClass=com.mysql.jdbc.Driver
- JDBC操作数据库
public class JDBC_Demo { private String userName = null; private String password = null; private String jdbcUrl = null; private String driverClass = null; private ResultSet resultSet =null; private PreparedStatement preparedStatement =null; private Connection connection =null; /** * 获取配置文件中的数据库信息 */ { ClassLoader classLoader2 = this.getClass().getClassLoader(); InputStream resourceAsStream = classLoader2.getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); try { properties.load(resourceAsStream); userName = properties.getProperty("db.username"); password = properties.getProperty("db.password"); jdbcUrl = properties.getProperty("db.jdbcUrl"); driverClass = properties.getProperty("db.driverClass"); } catch (IOException e) { e.printStackTrace(); } } @Test public void jdbcDemo () throws ClassNotFoundException { try { //第一步:加载数据库驱动 Class.forName(driverClass); //第二步:通过驱动管理类获取数据库链接 connection = DriverManager.getConnection(jdbcUrl, userName, password); //第三步:定义sql语句 ?表示占位符 String sql = "select * from items where id = ?"; //第四步:获取预处理statement preparedStatement = connection.prepareStatement(sql); //第五步:设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 preparedStatement.setInt(1, 1); //第六步:向数据库发出sql执行查询,查询出结果集 resultSet = preparedStatement.executeQuery(); //第七步:遍历查询结果集 while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " " + resultSet.getString("name")); } } catch (SQLException e) { e.printStackTrace(); }finally { //第八步:释放资源 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
-
-
Statement对象,Statement类的主要作用用于执行静态的SQL语句并且返回生成结果对象,通过Connection对象的createStatement()方法可以创建一个Statement对象。
-
调用Statement对象的相关方法执行响应的语句,execuUpdate()更新。
-
上边使用jdbc的原始方法(未经封装)实现了查询数据库表记录的操作。
-
-
创建并获取数据库链接
-
创建jdbc statement对象
-
设置sql语句
-
设置sql语句中的参数(使用preparedStatement)
-
通过statement执行sql并获取结果
-
对sql执行结果进行解析处理
-
1.2、jdbc问题总结如下
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
- Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
-
-
mybatis-config.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
-
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。mapper.xml文件需要在mybatis-config.xml中加载。
-
-
通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂。
-
由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
-
mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
-
Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
-
Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
-
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency>
CREATE DATABASE `mybatisdb` ;
USE `mybatisdb`;
DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL COMMENT '商品名称',
`price` FLOAT(10,1) NOT NULL COMMENT '商品定价',
`detail` TEXT COMMENT '商品描述',
`createtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '订单创建时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `items`(`id`,`name`,`price`,`detail`,`createtime`)
VALUES (1,'台式机',3000.0,'该电脑质量非常好!!!!','2015-02-03 13:22:53'),
(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!','2015-02-09 13:22:57'),
(3,'背包',200.0,'名牌背包,容量大质量好!!!!','2015-02-06 13:23:02');
DROP TABLE IF EXISTS `orderdetail`;
CREATE TABLE `orderdetail` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`orders_id` INT(11) NOT NULL COMMENT '订单id',
`items_id` INT(11) NOT NULL COMMENT '商品id',
`items_num` INT(11) DEFAULT NULL COMMENT '商品购买数量',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`)
VALUES (1,1,1,1),(2,2,2,2),(3,4,3,4),(4,4,2,3);
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL COMMENT '下单用户id',
`number` VARCHAR(32) NOT NULL COMMENT '订单号',
`createtime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓璁㈠崟鏃堕棿',
`note` VARCHAR(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `orders`(`id`,`user_id`,`number`,`createtime`,`note`)
VALUES (3,1,'1000010','2015-02-04 13:22:35',NULL),
(4,1,'1000011','2015-02-03 13:22:41',NULL),
(2,10,'1000012','2015-02-12 16:13:23',NULL);
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATE DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user`(`id`,`username`,`birthday`,`sex`,`address`)
VALUES
(1,'王五',NULL,'2',NULL),
(2,'张三','2014-07-10','1','北京市'),
(3,'张小明',NULL,'1','河南郑州')
根据用户id查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户
更新用户
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> <scope>provided</scope> </dependency> </dependencies>
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis默认使用log4j作为输出日志信息。
-
在classpath下创建mybatis-config_01.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> <!-- 和spring整合后 environments配置将废除--> <environments default="development"> <environment id="development"> <!-- 使用jdbc事务管理--> <transactionManager type="JDBC"/> <!-- 数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql:///mybatisdb?useSSL=false&serverTimezone=UTC"/> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> </configuration>
mybatis-config_01.xml是mybatis核心配置文件,上边文件的配置内容为数据源、事务管理。
IDEA方法参考:
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class User implements Serializable { private String id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 private List<Orders> orders; }
- 在classpath下的sqlmap目录下创建sql映射文件Users.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="user"> <!--user表字段--> <sql id="sqlColumnsForUserTable"> id,username,sex,birthday,address </sql> <!--结果集--> <resultMap type="com.jdy.mybatis2020.bean.User" id="userMap"> <id column="id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> <result column="birthday" property="birthday"/> <result column="address" property="address"/> </resultMap> <!--条件--> <sql id="sqlConditions"> <if test="username != null and '' != username"> <![CDATA[ AND username = #{username} ]]> </if> <if test="sex != null and '' != sex"> <![CDATA[ AND sex LIKE CONCAT('%','${sex}','%' ) ]]> </if> <if test="address != null and '' != address"> <![CDATA[ AND address = #{address} ]]> </if> <if test="birthday != null and '' != birthday"> <![CDATA[ AND birthday > DATE_FORMAT(#{birthday},'%Y-%m-%d') ]]> </if> </sql> <!-- 根据id获取用户信息 --> <select id="findUserById" resultType="com.jdy.mybatis2020.bean.User"> select <include refid="sqlColumnsForUserTable"/> from user where id = #{id}; </select> <!-- 模糊查询查询用户 --> <select id="findUserLikeUsername" resultType="com.jdy.mybatis2020.bean.User"> select <include refid="sqlColumnsForUserTable"/> from user where username like '%${value}%'; </select> <!-- 添加用户 --> <insert id="insertUser" parameterType="com.jdy.mybatis2020.bean.User"> <!-- selectKey将主键返回,需要再返回 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID(); </selectKey> insert into user(<include refid="sqlColumnsForUserTable"/>) values(#{id},#{username},#{sex},#{birthday},#{address}); </insert> <!-- 删除用户 --> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete> <!-- 更新用户 --> <update id="updateUser" parameterType="com.jdy.mybatis2020.bean.User"> update user <set> <if test="username != null"> username = #{username}, </if> <if test="birthday != null"> birthday = #{birthday}, </if> <if test="sex != null"> sex = #{sex}, </if> <if test="address != null"> address = #{address}, </if> </set> where id=#{id} </update> </mapper>
-
-
parameterType:定义输入到sql中的映射类型,#{id}表示使用preparedstatement设置占位符号并将输入变量id传到sql。mybatis通过ognl从输入对象中获取参数值拼接在sql中。
-
resultType
3.5、加载映射文件
mybatis框架需要加载映射文件,把user.xml添加在mybatis-config_01.xml,如下:
<mappers> <mapper resource="sqlmap/user.xml"/> </mappers>
public static SqlSessionFactory createFactory(String resource){ try { // 使用SqlSessionFactoryBuilder从xml配置文件中创建SqlSessionFactory InputStream inputStream = Resources.getResourceAsStream(resource); return new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } return null; }
- Test_00
public class Test_00 { public static final String RESOURCE = "mybatis-config/mybatis-config_01.xml"; private SqlSessionFactory sqlSessionFactory; @Before public void createFactory(){ sqlSessionFactory = SqlSessionFactoryUtil.createFactory(RESOURCE); } /** * 查询数据 */ @Test public void test_Method00() { // 数据库会话实例,一个SqlSession对象代表和数据库的一次会话 SqlSession sqlSession = null; try { // 创建数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 查询单个记录,根据用户id查询用户信息 User user = sqlSession.selectOne("user.findUserById", "1"); // 模糊查询 List<User> users = sqlSession.selectList("user.findUserLikeUsername", "小"); // 输出用户信息 System.out.println(user); System.out.println(users); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } /** * 添加数据 */ @Test public void test_Method01() { SqlSession sqlSession = null; try { // 创建数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用户信息 User user = new User(); user.setUsername("ws"); user.setAddress("陕西西安"); user.setSex("1"); user.setBirthday(new Date()); sqlSession.insert("user.insertUser", user); //提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } /** * 删除数据 */ @Test public void test_Method02() { // 数据库会话实例 SqlSession sqlSession = null; try { // 创建数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 删除用户 sqlSession.delete("user.deleteUserById", "3"); // 提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } /** * 修改数据 */ @Test public void test_Method03() { // 数据库会话实例 SqlSession sqlSession = null; try { // 创建数据库会话实例sqlSession sqlSession = sqlSessionFactory.openSession(); // 添加用户信息 User user = new User(); user.setId(29); user.setUsername("jdy"); user.setAddress("sxxn"); user.setSex("1"); user.setBirthday(new Date()); sqlSession.update("user.updateUser", user); // 提交事务 sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
#{}和${}的区别?
#{}是预编译处理,$ {}是字符串替换。
#{}表示一个占位符号,#{}是预编译处理,预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。#{parm}传入的数据都当成一个字符串,会对自动传入的数据加一个双引号
<insert id="insertUserNew" parameterType="com.jdy.mybatis2020.bean.User" useGeneratedKeys="true" keyProperty="id"> insert into user(username,birthday,sex,address)values(#{username},#{birthday},#{sex},#{address}) </insert>
而对于不支持自增型主键的数据库(例如Oracle),Oracle使用序列来模拟自增;每次插入的数据的主键是从序列中拿到的值。可以使用 selectKey 子元素(同时支持mysql):selectKey 元素将会首先运行, id 会被设置, 然后插入语句会被调用
<!-- 添加用户 --> <insert id="insertUser" parameterType="com.jdy.mybaties2020.bean.User"> <!-- selectKey将主键返回,需要再返回 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,birthday,sex,address)values(#{username},#{birthday},#{sex},#{address}) </insert>
添加selectKey实现将主键返回
-
-
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性。
-
order:取值BEFORE|AFTER。selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理,执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after。
-
resultType:返回的主键是什么类型。
-
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
-
<!--需要增加通过select uuid()得到uuid值--> <!--注意这里使用的order是“BEFORE”--> <insert id="insertUser" parameterType="com.jdy.mybaties2020.bean.User"> <selectKey resultType="java.lang.String" order="BEFORE" keyProperty="id"> select uuid() </selectKey> insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address}) </insert>
MyBatis是一个半自动化的持久化层框架。
与JDBC相比,JDBC的SQL夹在Java代码块里,耦合度高导致硬编码内伤, 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见。
与Hibernate和JPA相比,Hibernate和JPA 操作长难复杂SQL,对于而言处理也不容易,Hibernate内部自动生产的SQL,不容易做特殊优化,JPA基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降。
对开发人员而言,核心sql还是需要自己优化,MyBatis 将sql和java 编码分开,功能边界清晰,一个专注业务、一个专注数据 。
Mybatis与hibernate不同?
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。