使用JDBC操作数据库的问题总结:
(1)数据库连接,使用时创建,不使用时立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响数据库性能。
设想:使用数据库连接池管理数据库连接。
(2)将sql语句硬编译到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。
设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
(3)向preoaredStatement中设置参数,对占位符号位置和设置参数值,硬编译在java代码中,不利于系统维护。
设想:将sql语句和占位符号和参数全部配置在xml中。
(4)从resultSet中遍历集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护
设想:向查询的结果集,自动映射成java对象。
1、mybatis入门程序:
(1)第一步:导入jar包。mybatis核心jar包,lib依赖包,数据库驱动包。
(2)第二步:配置SqlMapConfig.xml文件:配置mybatis的运行环境,数据源,事务等。
<?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> <!-- 加载属性文件 --> <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.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </dataSource> </environment> </environments> </configuration>
db.properties文件:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///mybatis_01 jdbc.username=root jdbc.password=admin
(3)第三步:根据用户id(主键)查询用户信息
public class User { private int id; private String username; private String sex; private Date birthday; private String address; //下面省略get和set方法 }
映射文件(User.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"> <!-- namespace:命名空间,作用就是对sql进行分类化管理,理解sql隔离 注意:使用mapper代理方法开发,namespace有特殊重要的作用--> <mapper namespace="test"> <!-- 在映射文件中配置很多sql语句 --> <!-- 1.通过id查询数据库的记录 --> <!-- 通过select执行数据库查询 id:标识映射文件中的sql, 将sql语句封装到mappedStatement对象中,所以将id称为statement的id --> <!-- parameterType:指定输入参数的类型 --> <!-- #{}表示一个占位符号 #{id}:其中的id表示接收输入的参数,参数的名称就是id, 如果输入参数是简单类型,#{}中参数名可以任意,可以value或其他名称 --> <!-- resultType:指定sql输出结果的所映射java对象类型, select指定resultType表示将单条记录映射成的java对象 --> <select id="findUserById" parameterType="int" resultType="com.zwp.domain.User"> SELECT * FROM USER WHERE id=#{id} </select> <!-- 2.根据用户名称模糊查询用户信息,可能返回多条用户信息 resultType:指定就是单挑记录所映射的java对象类型 ${}:表示拼接sql串,接接收到的参数内容不加任何修饰拼接在sql中。 使用${value}拼接sql,容易引起sql注入。 ${value}:接收输入参数的内容,如果传入类型是简单类型,${}中只能使用value --> <select id="findUserByName" parameterType="java.lang.String" resultType="com.zwp.domain.User"> <!-- SELECT * FROM USER WHERE username like #{value} --> SELECT * FROM USER WHERE username like '%${value}%' </select> <!-- 3.向数据库插入数据 --> <insert id="insertUser" parameterType="com.zwp.domain.User"> <!-- 将插入数据的主键返回,返回到User对象中 SELECT LAST_INSERT_ID():只适用于自增主键 keyProperty:将查询到的主键值设置到parameterType指定对象的哪个属性 order:SELECT LAST_INSERT_ID()相对于insert语句的执行顺序 resultType:指定结果类型 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> SELECT LAST_INSERT_ID() </selectKey> <!--非自增主键的返回: 使用mysql的uuid()生成主键: <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String"> SELECT uuid() </selectKey> --> <!-- 通过oracle的序列生成主键 --> <!-- <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String"> SELECT 序列名.nextval() </selectKey> --> INSERT INTO USER(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <!-- 4.根据id删除用户 --> <delete id="deleteUser" parameterType="java.lang.Integer"> DELETE FROM USER WHERE id=#{id} </delete> <!-- 5.根据id更新用户 --> <update id="updateUser" parameterType="com.zwp.domain.User"> <!-- id必须存,要修改的用户id也包含在User对象中 --> UPDATE USER SET username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id=#{id} </update> </mapper>
(4)第四步:在SqlMapConfig.xml中加载映射文件
<mappers> <!-- 通过resource加载单个映射文件 --> <mapper resource="config/sqlmap/User.xml"></mapper> </mappers>
(5)第五步:编写程序:
public class MybatisFirst { //根据id得到一条用户记录 @Test public void findUserById() throws IOException{ //mybits配置文件 String resource="SqlMapConfig.xml"; //得到配置文件流 InputStream inputStream=Resources.getResourceAsStream(resource); //创建会话工厂 SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); //通过会话工厂得到SqlSession SqlSession sqlSession=sqlSessionFactory.openSession(); //通过SqlSession操作数据库 //第一个参数:映射文件中statement的id,等于namespace+"."+statement的id //第二个参数:指定和映射文件中所匹配的parameterType类型的参数 User user=sqlSession.selectOne("test.findUserById", 1); System.out.println(user); //释放资源 sqlSession.close(); } //根据用户名模糊查询 @Test public void findUserByName() throws IOException{ String resource="SqlMapConfig.xml"; InputStream inputStream=Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession=sqlSessionFactory.openSession(); //List<User> list=sqlSession.selectList("test.findUserByName", "%先生%"); List<User> list=sqlSession.selectList("test.findUserByName", "先生"); for(User user:list) { System.out.println(user.getId()+user.getUsername()); } sqlSession.close(); } //插入用户 @Test public void insertUser() throws IOException{ String resource="SqlMapConfig.xml"; InputStream inputStream=Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession=sqlSessionFactory.openSession(); User user=new User(); user.setUsername("张同学"); user.setBirthday(new Date()); user.setSex("男"); user.setAddress("汕头"); sqlSession.insert("test.insertUser",user); //事务提交 sqlSession.commit(); System.out.println(user.getId()); //打印插入后的user用户的id sqlSession.close(); } //根据用户id删除用户 @Test public void deleteUser() throws IOException{ String resource="SqlMapConfig.xml"; InputStream inputStream=Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession=sqlSessionFactory.openSession(); sqlSession.delete("test.deleteUser",6); sqlSession.commit(); sqlSession.close(); } //根据id修改用户 @Test public void updateUser() throws IOException{ String resource="SqlMapConfig.xml"; InputStream inputStream=Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession=sqlSessionFactory.openSession(); User user=new User(); user.setId(10);//id必须存在 user.setUsername("修改后的张同学"); user.setBirthday(new Date()); user.setSex("男"); user.setAddress("汕头"); sqlSession.update("test.updateUser",user); sqlSession.commit(); sqlSession.close(); } }
小结:
(1)parameterType:在映射文件中通过parameterType指定输入参数的类型
(2)resultType:在映射文件中通过resultType指定输出结果类型
(3)#{}和${}
#{}表示一个占位符号,参数可以是简单类型。参数是简单类型是时,命名无要求,可以是value或者其他值
${}表示一个拼接符号,会引入sql注入,所以不建议使用,参数可以是简单类型。参数是简单类型是时,命名只能是value。
(4)selectOne和selectList
–selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现,使用selectList也可以实现(List集合中只有一个对象)
–selectList表示查询出一个列表(多条记录)进行映射,如果使用selectList查询可以实现,不可以使用selectOne实现。
2、mybatis和hibernate本质区别和应用场景:
Hibernate:是一个标准的ORM框架(对象关系映射),不需要程序员写sql,sql语句自动生成了。对sql语句进行优化、修改比较困难
应用场景:使用于需求与变化不多的中小型项目,
Mybatis:专注于sql本身,需要程序员自己编写sql语句,修改比较方便。Mybatis是一个不完全的ORM框架,虽然需要程序员自己编写sql,mybatis也可以实现映射(输入映射,输入映射)
应用场景:使用于需求变化较多的项目,比如:互联网项目。
3、SqlSession:
SqlSession是一个面向用户的接口,线程不安去,在SqlSession实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。SqlSession最佳应用场合是在方法体内,定义成员局部变量使用。
4、原始dao开发方法:(程序员自己写dao和dao接口实现类)
(1)写dao接口和dao接口实现类:
(2)需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession。
//Dao接口
public interface UserDao {
//根据id查询用户
public User findUserById(int id) throws IOException;
//添加用户信息
//删除用户信息
}
//Dao接口实现类
public class UserDaoImpl implements UserDao{
//注入SqlSessionFactory对象,通过构造函数方法注入:
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws IOException {
SqlSession sqlSession=sqlSessionFactory.openSession();
User user=sqlSession.selectOne("test.findUserById", id);
sqlSession.close();
return user;
}
}
public class MybatisDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before//表示在其他方法之前执行
public void set() throws Exception
{
String resource="SqlMapConfig.xml";
//得到配置文件流
InputStream inputStream=Resources.getResourceAsStream(resource);
//创建会话工厂
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void findUserById() throws Exception
{
UserDao userdao=new UserDaoImpl(sqlSessionFactory);//注入sqlSessionFactory
User user=userdao.findUserById(10);
System.out.println(user.getId()+user.getUsername());
}
}
(3)总结原始dao开发问题:
①dao接口实现类方法中存在大量模板方法,加入能将这些代码提取出来,将会大大减轻程序员的工作量。
②调用sqlsession方法时将statement的id硬编码了。
③调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
5、Mapper代理开发方法:
(1)程序员需要编写mapper.xml文件
(2)程序员编写mapper接口(相当于dao接口),需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
(3)引入配置文件
开发规范:
①在mapper.xml中,namespace等于mapper接口地址
②mapper.java接口中方法名和mapper.xml中的statement的id一致
③mapper.java接口中方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
④mapper.java接口中方法的返回值类型和mapper.xml中statement中resultType指定类型一致。
public interface UserMapper {
/*
(1)mapper.java接口中方法名和mapper.xml中的statement的id一致
(2)mapper.java接口中方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
(3)mapper.java接口中方法的返回值类型和mapper.xml中statement的resultment中resultType指定类型一致。
*/
//根据id查询用户
public User findUserById(int id) throws IOException;
//根据用户名进行模糊查询
public List<User> findUserByName(String username) throws IOException;
//插入数据
public void insertUser(User user) throws IOException;
//根据用户Id删除用户
public void deleteUser(int id) throws IOException;
//根据用户id修改用户
public void updateUser(User user) throws IOException;
}
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void set() throws Exception
{
String resource="SqlMapConfig.xml";
//得到配置文件流
InputStream inputStream=Resources.getResourceAsStream(resource);
//创建会话工厂
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void test1() throws IOException
{
SqlSession sqlSession=sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.findUserById(10);
System.out.println(user.getUsername()+" "+user.getSex());
}
}
在SqlMapConfig.xml配置文件中加载映射文件:
<mappers>
<!-- 通过resource加载单个映射文件 -->
<mapper resource="config/sqlmap/User.xml"></mapper>
<mapper resource="config/sqlmap/UserMapper.xml"></mapper>
</mappers>
6、相关配置说明:
6.1、setting全局参数配置:
比如开启二级缓存,开启延迟加载
6.2、typeAliases(别名)–重点
在mapper.xml中,需要定义很多statement,在statement中,需要通过parameterType指定输入类型、需要通过resultType指定输出结果的映射类型。
如果在指定输入输出类型的全路径,不方便开发。可以通过在mapper.xml文件通过定义别名,方便开发
(1)单个定义
(2)批量定义
<!-- 定义别名 -->
<typeAliases>
<!-- 单个定义: type:类型的路径 alias:别名 -->
<typeAlias type="com.zwp.domain.User" alias="user" />
<!-- 批量定义:指定包名,mybatis自动扫描包中的类,自动定义别名,别名就是类名,首字母大小写都可以-->
<package name="com.zwp.domain"/>
</typeAliases>
6.3、typeHandlers(类型处理器):
mybatis中通过typeHandles完成jdbc类型和java类型的转化。通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义。
6.4、mappers(映射配置)
(1)通过resource加载单个映射文件
(2)使用class通过mapper接口加载映射文件
(3)使用package通过包名批量加载
<!-- 加载映射文件 -->
<mappers>
<!-- 通过resource加载单个映射文件 -->
<!-- <mapper resource="config/sqlmap/User.xml"></mapper> -->
<!-- <mapper resource="config/sqlmap/UserMapper.xml"></mapper> -->
<!-- 通过mapper接口加载单个映射文件
遵循规范:需要mapper接口类名和mapper.xml映射文件名称一致,且在同一目录下
上面规范的前提:使用mapper代理开发 -->
<!-- <mapper class="com.zwp.Dao.UserMapper"/> -->
<!-- 通过包名批量加载映射文件 -->
<!-- 指定包名,mybatis自动扫描包中的所有的mapper接口进行加载
遵循规范:需要mapper接口类名和mapper.xml映射文件名称一致,且在同一目录下
上面规范的前提:使用mapper代理开发 -->
<package name="com.zwp.Dao"/>
</mappers>
6.5、输入映射:
通过parameterType指定输入参数的类型,类型可以是简单参数,hashmap,pojo的包装类型。
传递pojo的包装对象:
需求:完成用户信息的综合查询,需要传入的查询条件很复杂(可能包括用户信息、其他信息,比如商品、订单的),针对上边的需求,建议使用自定义的包装类。
步骤:
(1)pojo包装类
//与数据库表相对应的User类:
public class User {
private int id;
private String username;
private String sex;
private Date birthday;
private String address;
//省略get和set方法
}
//User拓展类
public class UserCustomer extends User {
//可以在这个类中拓展用户信信息
}
//自定义的pojo包住类
public class UserQueryVo {
//在这里包装所需要查询的条件
private UserCustomer userCustomer;
public UserCustomer getUserCustomer() {
return userCustomer;
}
public void setUserCustomer(UserCustomer userCustomer) {
this.userCustomer = userCustomer;
}
//也可以包装其他的查询条件,订单、商品
}
(2)mapper.xml
(3)mapper.java
public interface UserMapper {
//用户综合查询
public List<UserCustomer> findUserList(UserQueryVo userQueryVo) throws IOException;
}
测试代码:
//用户综合查询
@Test
public void findUserListtest() throws IOException
{
SqlSession sqlSession=sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
UserQueryVo userQueryVo=new UserQueryVo();
UserCustomer userCustomer=new UserCustomer();
userCustomer.setSex("男");
userCustomer.setUsername("小鹏");
userQueryVo.setUserCustomer(userCustomer);
List<UserCustomer> list=userMapper.findUserList(userQueryVo);
System.out.println(list);
}
6.6、输出映射(resultType):
使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
如果查询出来的列名和pojo的属性有一个一致,就会创建pojo对象。
(1)输出简单类型:
用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。
查询出来的结果集只有一行且一列,就可以使用简单类型进行输出映射。
(2)输出pojo对象和pojo列表:
不管是输出的pojo单个对象还是一个列表,在mapper.xml中resultType指定的类型是一样的,在mapper.java指定的方法返回类型不一样。
①输出单个pojo对象,方法返回值是单个对象类型。
②输出pojo对象的list,方法返回值是List<Pojo>
生成的动态代理对象的过程中,是根据mapper方法的返回值类型确定是调用selectOne(返回单个对象调用)还是调用selectList(返回集合对象调用)
6.7输出映射(resultMap):
<!-- Type:resultMap最终映射的java类型,可以使用别名 id:resultMap的唯一标识 -->
<resultMap type="com.zwp.domain.User" id="resultMap1">
<!-- id:查询结果的唯一标识; column:查询出来的列名; property:type指定pojo对象属性名-->
<id column="id_" property="id"/>
<!-- 普通查询类型 -->
<result column="username_" property="username"/>
</resultMap>
<select id="findUserByIdResultMap" parameterType="int" resultMap="resultMap1">
SELECT id id_, username username_ FROM USER WHERE id=#{id}
</select>
小结:使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。
7、动态sql:
(1)if判断:
(2)sql片段:
–定义sql片段:
<!-- 定义sql片段 -->
<sql id="usersql">
<!-- 建议:1、sql片段最好基于单表;2、不要把where定义进来 -->
<if test="userCustomer!=null">
<if test="userCustomer.sex!=null">
AND user.sex=#{userCustomer.sex}
</if>
<if test="userCustomer.username!=null">
AND user.username LIKE '%${userCustomer.username}%'
</if>
</if>
</sql>
—引入sql片段:
<select id="findUserList" parameterType="com.zwp.domain.UserQueryVo" resultType="com.zwp.domain.UserCustomer" >
SELECT * FROM USER
<!-- where 会自动去除拼接条件的第一个AND -->
<where>
<!-- 引进sql片段 -->
<include refid="usersql"></include>
</where>
</select>
<!-- 综合查询总数: -->
<select id="findUserCount" parameterType="com.zwp.domain.UserQueryVo" resultType="int" >
SELECT COUNT(*) FROM USER
<where>
<include refid="usersql"></include>
</where>
</select>
(3)foreach遍历:
<if test="ids!=null">
<!-- collection:指定输入对象中属性集合 item:每次遍历生成的对象
open:开始遍历时拼接的串 close:结束遍历时拼接的串
separator:遍历的两个对象中需要拼接的串-->
<!-- 实现查询的sql语句:AND (id=1 OR id=5 OR id=7) -->
<foreach collection="ids" item="user_id" open="AND (" close=")" separator="OR">
id=#{user_id}
</foreach>
</if>
<if test="ids!=null">
<!-- 实现查询的sql语句:AND id In(1,5,7) -->
<foreach collection="ids" item="user_id" open="AND id IN(" close=")" separator=",">
#{user_id}
</foreach>
</if>
//自定义的pojo包住类
public class UserQueryVo {
//在这里包装所需要查询的条件
//用户查询条件
private List<Integer> ids;//添加条件集合
private UserCustomer userCustomer;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
public UserCustomer getUserCustomer() {
return userCustomer;
}
public void setUserCustomer(UserCustomer userCustomer) {
this.userCustomer = userCustomer;
}
//也可以包装其他的查询条件,订单、商品
}
//用户综合查询
@Test
public void findUserListtest() throws IOException
{
SqlSession sqlSession=sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
UserQueryVo userQueryVo=new UserQueryVo();
UserCustomer userCustomer=new UserCustomer();
userCustomer.setSex("男");
userCustomer.setUsername("小鹏");
List<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(5);
ids.add(7);
userQueryVo.setIds(ids);
userQueryVo.setUserCustomer(userCustomer);
int count=userMapper.findUserCount(userQueryVo);
List<UserCustomer> list=userMapper.findUserList(userQueryVo);
System.out.println(count);
System.out.println(list);
}
下篇:mybatis框架–学习笔记(下):https://blog.csdn.net/a745233700/article/details/81035781
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/114729.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...