发表于: 2017-10-02 22:55:41
1 874
今日完成:
对MAVEN项目中的路径问题成功解决,
注意点1 在applicationContext.xml中注入bean元素时 class内 存在 默认路径前缀src/main/java
注意点二 在读取applicationContext.xml时依旧存在默认路径前缀src/main/resources
这里重写了Emp类中的toString 方法对其赋值进行输出,测试成功。
bean 对象创建时还可以在 class后添加其他属性,其中有scope范围属性,在不设置值的情况下默认为singleton,及单例模式,
@Test
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp1 = (Emp) ac.getBean("emp");
Emp emp2 = (Emp) ac.getBean("emp");
emp1.setId(1);
System.out.println(emp2.getId());
}
这里测试结果为1,使用getBean得到的两个emp为同一个对象,将emp1赋值id1,用emp2对象getid值为1;
其中配置文件中的bean对象emp在ioc容器初始化之前也就是
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
就会创建, 这种模式适合用来创建dao/service;
scope属性也可以设置成prototype为多例模式,和上面相反,两个emp为不同对象,且对象在getBean使用时才会创建,适合创建action。
对象依赖首先要了解bean中的property属性
name指的是class类中的属性的字段名称 这里我emp中含有id属性,value代表着直接对该属性进行赋值,ref代表着该属性的引用,但是前提是一定要对name中对应的属性字段设置相应的set方法 否则无法使用!
再引申一点如果我emp中含的是另外一个类比如说含有dinner,那么
<bean id="dinner" class="com.Dinner"></bean>
<bean id="emp" class="com.Emp">
<property name="dinner" ref="dinner"></property>
</bean>
使用了依赖注入
package com;
public class Dinner {
public Dinner() {
}
public void say(){
System.out.println("dinner!");
}
}
在Emp中添加
private Dinner dinner ;
public void setDinner(Dinner dinner) {
this.dinner = dinner;
}
public void testdinner(){
dinner.say();
}
测试OK!红字放大的部分一定要一致,大小写区分!
因配置过于麻烦这里学习注解这一大神器
@Component放在类上方等同于<bean id="我的类" class="我的类对应的包"></bean>也就是把类注入当前IOC容器,后面可以跟上
(“bean中你的ID”),如不写括号内容会默认设置成你原来的类名 首字母小写注入到ioc中
@Component
public class Emp{。。。。。。。。}
等于<bean id="emp" class="emp对应的包"></bean>
@Resource 等同于<property name="XXX" ref="XXX"></property>
@Resource
private Dinner dinner ;
public void testdinner(){
dinner.say();
}
相同的@Resource(name="XXXX") 括号内容会自动根据上方加粗红题字去ioc容器中寻找该id的bean并注入,同时set方法可以省去,大大优化了代码
那么程序怎么识别这些注解呢?在配置文件中开启注解扫描
配置文件中内容前所未有的清爽,然后按照前一个测试代码测试 OK!
控制反转,依赖注入,然后就是难度颇大的AOP了,因理解难度过高,且IOC和DI已经可以使用spring jdbc进行连接了,AOP暂时标记stop,
<!--开启注解扫描!-->
<context:component-scan base-package="com"></context:component-scan>
<!--配置C3P0数据连接源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydemo"></property>
<property name="user" value="root"></property>
<property name="password" value="8866521"></property>
<property name="initialPoolSize" value="5"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxIdleTime" value="1000"></property>
</bean>
<!--将我的数据源注入jdbcTemplate中-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
@Test
public void test1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDao dao = (TestDao) ac.getBean("testDao");
//dao.deletebyid(24);
//dao.add(101,"胡汉三");
dao.update("刘能!",101);
}
这里采用了通配符进行对数据的增删改操作,但是使用jdbctemplate 要注意的是 在查询返回的结果处理 有所不同。
当返回结果值是唯一的时候可以使用queryForMap将查询的结果已键值对保存,
当返回多个结果时可以用queryForList,将每一条结果封装成Map在将Map添加到List集合中便于取出
public Map<String,Object> selectbyid(int i){
String sql = "select * from user where id = ?";
Map<String,Object> m = jdbcTemplate.queryForMap(sql,i);
return m;
}
public List<Map<String, Object>> findAll(){
String sql ="select * from user";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return list;
}
这里再次进行测试 返回结果OK,可以正常打印!
其中在jdbcTemplate.query()该方法中提供了N个重载方法,其中有
List<T> list =jdbcTemplate.query(sql, new RowMapper<T>() {
@Override
public Emp mapRow(ResultSet resultSet, int i) throws SQLException {
T t = new T();
t.setId(resultSet.getInt("XXX"));
t.setName(resultSet.getString("XXX"));
return t;
}
}) ;
这个是指执行sql语句后,返回ResultSet结果,这个东西就很熟悉了,取值的方法和jdbc中一模一样,i是表示取值时每一行的列号索引当然下标从1开始,依旧的不推荐使用,这里我使用了列名“XXXX” 取值,取出后,因为我的程序中没有和数据库对应的实体类,那么我偷了个懒,把user表中的id,和name赋值给了emp的id和name进行测试,方法执行完后,把emp放到集合中retrun 看下源码及测试结果
public List<Emp> testAll(){
String sql = "select * from user";
List<Emp> list =jdbcTemplate.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet resultSet, int i) throws SQLException {
Emp emp = new Emp();
emp.setId(resultSet.getInt("id"));
emp.setName(resultSet.getString("name"));
return emp;
}
}) ;
return list;
}
虽然重写方法内需要手动赋值比较麻烦,但是涉及具体需求时此方法较为灵活,感觉能用到。
那么下一步就来到了Mybatis对数据库进行连接,学一款框架最好的方式就是先跑起来!
先对mybatis进行总配置文件设置,其中enviroments标签下可同时拥有多个enviroment数据库连接源,每一个连接源都有Id值作为区分,在下面红字部分指定了Mysql的连接,事务管理类型为jdbc 数据源为连接池方式
<?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>
<environments default="mysql_development">
<environment id="mysql_development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydemo"/>
<property name="username" value="root"/>
<property name="password" value="8866521"/>
</dataSource>
</environment>
</environments>
</configuration>
设置好后,进行了尝试连接,看是否能取出我们需要的Connection对
public class testMybatis {
@Test
public void test(){
//InputStream is = testMybatis.class.getClassLoader().getResourceAsStream("mybatis.xml");
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
Connection connection = sqlSessionFactory.openSession().getConnection();
}
}
其中对配置文件加载的方式有两种,一是被红字注释掉的类加载方式,还有一个种就是Mybatis自己提供的Resource类加载方式,均测试成功连接到数据库!
但是要对其数据库内的内容进行增删改查,就得新建实体类和使用该类的映射文件,因为user表中的字段过多,这里为了方便就新建了一个表,只有3个字段
create table student(
id int PRIMARY KEY,
name varchar(50),
ability int
);
在src/main/java/com目录中创建和数据库中对应的实体类, 这里为了区分哪个是实体类中的属性哪个是数据中的属性做了区分,在实体类属性前加了“s_”
public class Student {
private int s_id;
private String s_name;
private int s_ability;
public Student() {
}
public int getS_id() {
return s_id;
}
public void setS_id(int s_id) {
this.s_id = s_id;
}
public String getS_name() {
return s_name;
}
public void setS_name(String s_name) {
this.s_name = s_name;
}
public int getS_ability() {
return s_ability;
}
public void setS_ability(int s_ability) {
this.s_ability = s_ability;
}
}
创建完后配置映射文件
<?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="cn.cccy.student">//在namespace中红字标记的称为名称空间,名字可以随意取但必须是唯一的,
<resultMap type="com.Student" id="mystudent"> //核心映射内容,type指向student包类
<id property="s_id" column="id"/> //与数据对应的内容 property填写student实体类中的名字 column填写数据库中对应列的名字 最前面id指的是主键!
<result property="s_name" column="name"/> //普通字段对应,顺序不要弄反
<result property="s_ability" column="name"/> //和上面一样 普通字段对应
</resultMap>
<insert id="add" > //重头戏 将制定sql语句放入其中,id是干什么用呢?往下看
insert into student(id,name,ability) values (3,"测试1",999);
</insert>
</mapper>
配置文件中要注意的有很多,写的不全,但使用类加载配置文件时只会加载总配置文件,那么就得把映射文件注册到总配置文件中,在总配置文件中添加
<mappers>
<mapper resource="student-mybatis.xml"></mapper>
</mappers>
好了万事俱备只欠东风,正式开始增删改查
@Test
public void test() throws Exception {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("cn.cccy.student.add");
sqlSession.commit();
}
在核心类sqlSession与数据库会话中,括号里的内容取代了传统的sql语句改变成 映射文件中mapper的名称空间加一个点,跟上mapper中定义的sql语句的id来执行,最后提交任务成功
开始熟悉其他操作数据库的方法
<insert id="add2" parameterType="com.Student">
insert into student(id,name,ability) values (#{s_id},#{s_name},#{s_ability});
</insert>
这里插入新的学生时,直接选择了插入一个学生对象,这种完全面向编程的方式很友好,在传统sql语句中的通配符? 被#{XX} 代替,
parameterType指定了我查询对象的类型,花括号中橙色的内容就会去该对象中去找该属性,测试成功。
在sql条件中使用普通字符串作为判定条件时#{}花括号内容可以随意填写如
<delete id="delete1" parameterType="int">
delete from student where id = #{xxxx}
</delete>
@Test
public void test2() throws Exception{
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("cn.cccy.student.delete1",3);
sqlSession.commit();
}
虽然测试成功但是,花括号内容还是尽量写近义的单词,保持良好的代码习惯!
再测试查询方法
<select id="findAll" parameterType="int" resultType="com.Student">
select id,name,ability from student where id=#{id};
</select>
因为只要查询就会就结果返回,那么resultType就是指定你返回的结果类型
@Test
public void test3() throws Exception{
Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
Student student= sqlSession.selectOne("cn.cccy.student.findAll",2);
System.out.println(student.getS_id()+student.getS_name());
}
喜闻乐见测试失败,最常见的空指针异常
纠结了半小时解决
原理很简单,在我使用select id,name,ability from student查询完后,每一个对应结果都将自动封装进我的实体类student中,那么在封装时,就会按照列名使用 set方法赋值 比如 setID/setName...但是前面为了区分java中的实体类和数据库中的列命重合而特意写的不一样,这就造成我在赋值时失败,student为空值,做以下修改
<select id="findAll" parameterType="int" resultType="com.Student">
select id 's_id',name 's_name',ability 's_ability' from student where id=#{id};
</select>
机智的对查询结果列的列名进行修改,终于测试成功!
那么mybatis的基础入门,增删改查完成,关于动态SQL和N对N关联映射暂时标记STOP。
为了方便测试类型结构很不清晰,按照规范修改
public interface SpringDao {
public void add();
public void delete();
public void update();
}
@Component()
public class SpringDaoImpl implements SpringDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public void add() {
String sql = "insert into student(id,name) values(33,'王五')";
jdbcTemplate.update(sql);
}
@Override
public void delete() {
String sql = "delete from student where id=33";
jdbcTemplate.update(sql);
}
@Override
public void update() {
String sql = "update student set name='刘能!' where id=2";
jdbcTemplate.update(sql);
}
}
@Test
public void test2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringDao dao = (SpringDao) ac.getBean("springDaoImpl");
//dao.delete();
//dao.add();
dao.update();
}
测试成功!
@Test
public void test() throws Exception {
StudentDao.add();
}
OK,这里没有对SqlSession的取出做抽取方法,所以dao方法里内容较多。
接口和实现为什么要分离可理解为→→→本来我就是一个类为什么要搞出一个接口呢再理解为→→→→接口有什么好处我为什么要去用它?
OK,套用知乎大大的原话
接口就是个招牌。
比如说你今年放假出去杭州旅游,玩了一上午,你也有点饿了,突然看到前面有个店子,上面挂着KFC,然后你就知道今天中饭有着落了。
KFC就是接口,我们看到了这个接口,就知道这个店会卖炸鸡腿(实现接口)。
那么为神马我们要去定义一个接口涅,这个店可以直接卖炸鸡腿啊(直接写实现方法),是的,这个店可以直接卖炸鸡腿,但没有挂KFC的招牌,我们就不能直接简单粗暴的冲进去叫服务员给两个炸鸡腿了。
要么,我们就要进去问,你这里卖不卖炸鸡腿啊,卖不卖汉堡啊,卖不卖圣代啊(这就是反射)。很显然,这样一家家的问实在是非常麻烦(反射性能很差)。要么,我们就要记住,中山路108号卖炸鸡,黄山路45号卖炸鸡(硬编码),很显然这样我们要记住的很多很多东西(代码量剧增),而且,如果有新的店卖炸鸡腿,我们也不可能知道(不利于扩展)。
作者:Ivony
链接:https://www.zhihu.com/question/20111251/answer/16585393
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
接口与实现的分离优点解耦合,提升程序拓展能力,
但为什么Mybatis不需要impl呢? 因为他的具体实现功能是在xml文件配置中,在mybatis中的dao如sqlSession.xxx("")功能 就等同于jdbc template中的dao接口了,所以在Mybatis中不需要impl
天色已晚快11点了,整理整理差不多到步骤21了。
明日计划:今天标记的两处aop 和mybatis深入学习,依旧坚定不移的推进task1 完成!
问题: 在正常项目以maven骨架开发时,我的实体类映射文件照道理来说应该和实体类放一块儿?那路径的问题又要纠结了。 望解答。。spring和mybatis整合暂时没头绪。
收货:很好的复习了ioc di spring jdbc mybatis的基本使用。对接口的作用好好的上了一课
评论