SSM
MyBatis
——如果世界总是这么简单就好了
MyBatis 是一款优秀的持久层框架 ,它支持自定义 SQL、存储过程以及高级映射。简化JDBC。
MyBatis官方文档
Maven
1
2
3
4
5
6
<!-- https : //mvnrepository.com/artifact/org.mybatis/mybatis -->
< dependency >
< groupId > org . mybatis </ groupId >
< artifactId > mybatis </ artifactId >
< version > 3.5.7 </ version >
</ dependency >
持久层
完成持久化的代码块
持久化:程序在持久状态和瞬时状态转换的过程
在内存里断电就消失
数据库(JDBC)、IO文件持久话
第一个MyBatis
从 XML 中构建 SqlSessionFactory
在src–>resources中创建MyBatis-config.xml
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
1.配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!---------Mybatis-config.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:核心配置文件-->
<configuration>
<environments default= "development" >
<environment id= "development" >
<transactionManager type= "JDBC" />
<dataSource type= "POOLED" >
<!--使用的数据库的driver,此处为mysql-->
<property name= "driver" value= "com.mysql.cj.jdbc.Driver" />
<!--url为一些基础配置,对应数值的意思见下表-->
<property name= "url" value= "jdbc:mysql://localhost:3306/db_library?useSSL=true&serverTimezone=UTC" />
<property name= "username" value= "root" /> <!--sql用户名-->
<property name= "password" value= "zyyxy123" /> <!--sql密码-->
</dataSource>
</environment>
</environments>
<mappers>
<!--注册mapper文件,设置xxMapper.xml文件的具体位置-->
<mapper resource= "com/mhqdz/dao/db_libraryMapper/Db_libraryMapper.xml" />
</mappers>
</configuration>
2.pojo资源类
使用Lombok的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.mhqdz.pojo ;
public class Db_library {
private String name ;
private int id ;
private int lv ;
public Db_library ( String name , int id , int lv ) {
this . name = name ;
this . id = id ;
this . lv = lv ;
}
public String getName () {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public int getId () {
return id ;
}
public void setId ( int id ) {
this . id = id ;
}
public int getLv () {
return lv ;
}
public void setLv ( int lv ) {
this . lv = lv ;
}
@Override
public String toString () {
return "Db_library{" +
"name='" + name + '\'' +
", id=" + id +
", lv=" + lv +
'}' ;
}
}
3.创建工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//存放工具类SqlSessionFactory:创建SqlSession实例的工厂
//SqlSession:提供在数据库执行SQL命令所需的方法。
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory ;
static {
String resource = "Mybatis-config.xml" ;
try {
InputStream inputStream = Resources . getResourceAsStream ( resource );
sqlSessionFactory = new SqlSessionFactoryBuilder (). build ( inputStream );
} catch ( IOException e ) {
e . printStackTrace ();
}
}
public static SqlSession GetSqlSession (){
return sqlSessionFactory . openSession ();
}
}
4.Dao接口
1
2
3
public interface Db_libraryMapper {
List < Db_library > getDb_libraryList ();
}
5.接口实现类
1
2
3
4
5
6
7
8
9
10
<?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= "com.mhqdz.dao.db_libraryMapper.Db_libraryMapper" > <!--命名空间:传入接口的位置-->
<!--id为该操作命名,resultType输出结果集的模板-->
<select id= "getDb_libraryList" resultType= "com.mhqdz.pojo.Db_library" >
select * from db_library.user
</select>
</mapper>
增删改查功能改进
接口
1
2
3
4
5
6
7
8
public interface Db_libraryMapper {
List < Db_library > getDb_libraryList (); //列出用户
Db_library getUserById ( int id ); //通过id查询用户
int addDb_library ( Db_library db_library ); //添加用户
int deleteDb_libraryById ( int id ); //通过id删除用户
int updateDb_libraryById ( int id ); //通过id更改用户
List < Db_library > getDb_libraryListLike ( String value ); //模糊查询
}
xml实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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= "com.mhqdz.dao.db_libraryMapper.Db_libraryMapper" >
<select id= "getUserById" parameterType= "int" resultType= "com.mhqdz.pojo.Db_library" >
select * from db_library.user where id = #{id}
</select>
<select id= "getDb_libraryList" resultType= "com.mhqdz.pojo.Db_library" >
select * from db_library.user
</select>
<!--select查询,insert添加-->
<insert id= "addDb_library" >
insert into db_library.user (name,id,lv)values(#{name},#{id},#{lv});
</insert>
<delete id= "deleteDb_libraryById" >
delete from db_library.user where id=#{id};
</delete>
<update id= "updateDb_libraryById" >
update db_library.user set name=#{name},lv=#{lv} where id=#{id}
</update>
<select id= "getDb_libraryListLike" resultType= "com.mhqdz.pojo.Db_library" >
select * from db_library.user where name like #{value};
</select>
</mapper>
test文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.mhqdz.dao.db_libraryMapper ;
import com.mhqdz.pojo.Db_library ;
import com.mhqdz.utils.MybatisUtils ;
import org.apache.ibatis.session.SqlSession ;
import org.junit.jupiter.api.Test ;
import java.util.List ;
public class Db_libraryMapperText {
@Test
public void getDb_libraryTest () {
//得到sqlSession对象
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
//执行sql
//法1:getMapper
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
List < Db_library > db_libraryList = mapper . getDb_libraryList ();
//法2:直接使用,不推荐
//List<Db_library> db_libraryList = sqlSession.selectList("getDb_libraryList");
for ( Db_library db_library : db_libraryList ) {
System . out . println ( db_library . toString ());
}
sqlSession . close ();
}
@Test
public void getUserByIdTest () {
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
Db_library user = mapper . getUserById ( 17 );
System . out . println ( user . toString ());
sqlSession . close ();
}
@Test
public void addDb_libraryTest () {
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int change = mapper . addDb_library ( new Db_library ( "joker" , 18 , 6 ));
if ( change > 0 ) {
System . out . println ( "插入成功" );
} else {
System . out . println ( "插入失败" );
}
sqlSession . commit (); //提交
sqlSession . close ();
}
@Test
public void deleteDb_libraryByIdTest () {
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int i = mapper . deleteDb_libraryById ( 18 );
if ( i > 0 ) {
System . out . println ( "删除成功" );
} else {
System . out . println ( "删除失败" );
}
sqlSession . commit ();
sqlSession . close ();
}
@Test
public void updateDb_libraryByIdTest (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int change = mapper . updateDb_libraryById ( 18 );
if ( change > 0 )
{
System . out . println ( "修改成功" );
} else {
System . out . println ( "修改失败" );
}
sqlSession . commit (); //提交
sqlSession . close ();
}
@Test
public void getDb_libraryLikeTest () {
//得到sqlSession对象
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
List < Db_library > db_libraryList = mapper . getDb_libraryListLike ( "%m%" );
for ( Db_library db_library : db_libraryList ) {
System . out . println ( db_library . toString ());
}
sqlSession . close ();
}
}
用Map传值
借用Map可以随意的传值,如果参数有多个可以考虑使用Map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//接口
int addDb_libraryMap ( Map < String , Object > map );
//xml
< insert id = "addDb_libraryMap" parameterType = "map" >
insert into db_library . user ( name , id , lv ) values ( # { n }, # { i }, # { l })
</ insert >
//test
@Test
public void addDb_libraryMapTest () {
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
Map < String , Object > map = new HashMap <>();
map . put ( "i" , 18 );
map . put ( "l" , 1 );
map . put ( "n" , "rose" );
int change = mapper . addDb_libraryMap ( map );
if ( change > 0 ) {
System . out . println ( "插入成功" );
} else {
System . out . println ( "插入失败" );
}
sqlSession . commit (); //提交
sqlSession . close ();
}
Map是万能的,但不正规。
配置解析
属性(properties)优化
在resoruces
文件夹下(好处是与Mybatis-config.xml
处在同文件夹,在Mybatis-config.xml
中引入它的时候可以省略目录),新建一个properties
文件保存属性,使这些属性可以动态替换
1
2
3
4
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/db_library?useSSL=true&serverTimezone=UTC
username = root
password = zyyxy123
引入
在Mybatis-config.xml
中引入配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<properties resource= "db.properties" />
或是不引用属性文件,直接将所需要的属性写入properties标签,如:
<properties>
<property name= "driver" value= "com.mysql.jdbc.Driver" />
<property name= "url" value= "jdbc:mysql://localhost:3306/db_library?useSSL=true&serverTimezone=UTC" />
<property name= "username" value= "root" />
<property name= "password" value= "zyyxy123" />
</properties>
两种都用上……也不是不行
<properties resource= "db.properties" >
<property name= "driver" value= "com.mysql.jdbc.Driver" />
<property name= "password" value= "zyyxy123" />
</properties>
<properties/>
标签必须写在<configuration>
的第一个
如果既引用了外部文件又写了相同的字段则使用配置文件的
将dataSource
改成${driver}
、${url}
……这样的形式就可以实现动态替换了
1
2
3
4
5
6
<dataSource type= "POOLED" >
<property name= "driver" value= "${driver}" />
<property name= "url" value= "${url}" />
<property name= "username" value= "${username}" />
<property name= "password" value= "${password}" />
</dataSource>
别名优化
在Mybatis-config.xml
中给类取别名为user
1
2
3
<typeAliases>
<typeAlias type= "com.mhqdz.pojo.Db_library" alias= "user" />
</typeAliases>
也可以直接给一个包,它会将包下的类全都命别名,默认别名是它的首字母小写,如:
1
2
3
<typeAliases>
<package name= "com.mhqdz.pojo" />
</typeAliases>
此时类com.mhqdz.pojo.Db_library
就被取了别名为db_library
ps:使用Db_library也可以调用到,不过官方不推荐
如果在该类上有注解Alias(“别名”),则会被命名为注解里的别名
设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<settings>
<setting name= "cacheEnabled" value= "true" />
<setting name= "lazyLoadingEnabled" value= "true" />
<setting name= "multipleResultSetsEnabled" value= "true" />
<setting name= "useColumnLabel" value= "true" />
<setting name= "useGeneratedKeys" value= "false" />
<setting name= "autoMappingBehavior" value= "PARTIAL" />
<setting name= "autoMappingUnknownColumnBehavior" value= "WARNING" />
<setting name= "defaultExecutorType" value= "SIMPLE" />
<setting name= "defaultStatementTimeout" value= "25" />
<setting name= "defaultFetchSize" value= "100" />
<setting name= "safeRowBoundsEnabled" value= "false" />
<setting name= "mapUnderscoreToCamelCase" value= "false" />
<setting name= "localCacheScope" value= "SESSION" />
<setting name= "jdbcTypeForNull" value= "OTHER" />
<setting name= "lazyLoadTriggerMethods" value= "equals,clone,hashCode,toString" />
< /settings
Mapper(映射器)
注册映射接口
法一:
1
2
3
4
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource= "com/mhqdz/dao/db_libraryMapper/Db_libraryMapper.xml" />
</mappers>
法二:
1
2
3
4
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class= "com.mhqdz.dao.db_libraryMapper.Db_libraryMapper" />
</mappers>
法三:
1
2
3
<mappers>
<package name= "com.mhqdz.dao" />
</mappers>
注意点:
接口文件和它的Mapper配置文件必须在一个包下
接口文件和它的Mapper配置文件必须同名
其他配置
作用域(Scope)和生命周期
生命周期类别是至关重要的,关的不及时就会并发不安全
SqlSessionFactoryBuilder :一旦创建了 SqlSessionFactory,就不再需要它了
SqlSessionFactory :一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
SqlSession :每个线程都应该有它自己的 SqlSession 实例 SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到try/catch的finally块中
日志
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging
1
2
3
4
5
6
7
8
<configuration>
<settings>
...
<setting name= "logImpl" value= "LOG4J" />
...
</settings>
</configuration>
分页、log4j丢了,当时保存了,但没有安全保存。
使用注解增删改查
注解
1
2
3
4
在config里配置mapper,给出Mapper接口集合的位置
<mappers>
<mapper class= "com.mhqdz.dao.db_libraryMapper.Db_libraryMapper" />
</mappers>
1
2
3
在接口集合里的接口上面注解写上 sql 语句 , 就可以用了
@Select ( "select * from db_library.user where id=#{id}" )
Db_library getDb_libraryById ( @Param ( "id" ) int id ); //@Param()注释基本类型,可以用它传多个参数,详见下面的@Param()注解
增删改查
接口db_libraryMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.mhqdz.dao.db_libraryMapper ;
import com.mhqdz.pojo.* ;
import org.apache.ibatis.annotations.* ;
import java.util.List ;
public interface Db_libraryMapper {
@Select ( "select * from db_library.user" )
List < Db_library > getDb_libraryList ();
@Select ( "select * from db_library.user where id=#{id}" )
Db_library getDb_libraryById ( @Param ( "id" ) int id ); //@Param()注释基本类型,可以用它传多个参数
@Delete ( "delete from db_library.user where id=#{id}" )
int DeleteDb_libraryById ( @Param ( "id" ) int id );
@Insert ( "insert into db_library.user (name,id,lv)values(#{name},#{id},#{lv})" )
int AddDb_library ( Db_library db_library );
@Update ( "update db_library.user set name=#{name},lv=#{lv} where id=#{id}" )
int UpdateDb_libraryById ( Db_library db_library );
}
测试类db_libraryMapperTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.mhqdz.dao.db_libraryMapper ;
import com.mhqdz.pojo.Db_library ;
import com.mhqdz.utils.MybatisUtils ;
import org.apache.ibatis.session.SqlSession ;
import org.junit.jupiter.api.Test ;
import java.util.List ;
class Db_libraryMapperTest {
@Test
public void getDb_libraryListTest (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
List < Db_library > list = mapper . getDb_libraryList ();
for ( Db_library db_library : list ){
System . out . println ( db_library );
}
sqlSession . close ();
}
@Test
public void getDb_libraryByIdTest (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
Db_library db_library = mapper . getDb_libraryById ( 17 );
System . out . println ( db_library );
sqlSession . close ();
}
@Test
void DeleteDb_libraryByIdTest (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int a = mapper . DeleteDb_libraryById ( 13 );
System . out . println ( a );
sqlSession . close ();
}
@Test
public void AddDb_libraryTest (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int a = mapper . AddDb_library ( new Db_library ( "a" , 13 , 10 ));
System . out . println ( a );
sqlSession . close ();
}
@Test
public void UpdateDb_libraryText (){
SqlSession sqlSession = MybatisUtils . GetSqlSession ();
Db_libraryMapper mapper = sqlSession . getMapper ( Db_libraryMapper . class );
int a = mapper . UpdateDb_libraryById ( new Db_library ( "b" , 13 , 11 ));
System . out . println ( a );
sqlSession . close ();
}
}
配置文件Mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//d.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration:核心配置文件-->
<configuration>
<properties resource= "db.properties" />
<typeAliases>
<package name= "com.mhqdz.pojo" />
</typeAliases>
<environments default= "development" >
<environment id= "development" >
<transactionManager type= "JDBC" />
<dataSource type= "POOLED" >
<property name= "driver" value= "${driver}" />
<property name= "url" value= "${url}" />
<property name= "username" value= "${username}" />
<property name= "password" value= "${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class= "com.mhqdz.dao.db_libraryMapper.Db_libraryMapper" />
</mappers>
</configuration>
其他
@Param()注解
概况
基本类型或String类型需要加上
如果只有一个基本类型或String类型可以不写,但最好写上
SQL语句会使用@Param()里命名的变量名
#{}和${}
概况
1)#{}是预编译处理,$ {}是字符串替换。
2)MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
3)使用 #{} 可以有效的防止SQL注入,提高系统安全性。
Lombok
Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。官方地址 ,github地址 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
方法
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor , @RequiredArgsConstructor and @NoArgsConstructor
@Log , @Log4j , @Log4j2 , @Slf4j , @XSlf4j , @CommonsLog , @JBossLog , @Flogger , @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
常用
@Data:添加:无参构造,set&get方法,toString(),equals(Object),hashCode()
@AllArgsConstructor:添加:有参构造,默认添加有参后会把无参删掉,如果还需要无参就使用@NoArgsConstructor
@NoArgsConstructor:添加:无参构造
@ToString:添加:toString
@EqualsAndHashCode:添加:equals(Object),hashCode()
例如:该文档最初的pojo资源类 使用了Lombok后:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Db_library {
private String name ;
private int id ;
private int lv ;
}
总结:Lombok是一个能够简化我们操作的工具,它会破坏代码的格式,在使用了它后源码会对新人不太友好,不推荐在pojo以外的地方使用
一对多、多对一的处理
参考文章
环境搭建
数据库建立
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE ` teacher ` (
` id ` INT ( 10 ) NOT NULL ,
` name ` VARCHAR ( 30 ) DEFAULT NULL ,
PRIMARY KEY ( ` id ` )
) ENGINE = INNODB DEFAULT CHARSET = utf8 ;
INSERT INTO teacher ( ` id ` , ` name ` ) VALUES ( 1 , '秦老师' );
CREATE TABLE ` student ` (
` id ` INT ( 10 ) NOT NULL ,
` name ` VARCHAR ( 30 ) DEFAULT NULL ,
` tid ` INT ( 10 ) DEFAULT NULL ,
PRIMARY KEY ( ` id ` ),
KEY ` fktid ` ( ` tid ` ),
CONSTRAINT ` fktid ` FOREIGN KEY ( ` tid ` ) REFERENCES ` teacher ` ( ` id ` )
) ENGINE = INNODB DEFAULT CHARSET = utf8 ;
INSERT INTO ` student ` ( ` id ` , ` name ` , ` tid ` ) VALUES ( 1 , '小明' , 1 );
INSERT INTO ` student ` ( ` id ` , ` name ` , ` tid ` ) VALUES ( 2 , '小红' , 1 );
INSERT INTO ` student ` ( ` id ` , ` name ` , ` tid ` ) VALUES ( 3 , '小张' , 1 );
INSERT INTO ` student ` ( ` id ` , ` name ` , ` tid ` ) VALUES ( 4 , '小李' , 1 );
INSERT INTO ` student ` ( ` id ` , ` name ` , ` tid ` ) VALUES ( 5 , '小王' , 1 );
Teacher和Student类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Teachar类
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
@AllArgsConstructor
@Data
public class Teacher {
private int id ;
private String name ;
}
//Student类
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
@AllArgsConstructor
@Data
public class Student {
private int id ;
private String name ;
private Teacher teacher ;
}
TeacherMapper和StudentMpper接口类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//TeacherMapper
package com.mhqdz.dao ;
import com.mhqdz.pojo.Teacher ;
import org.apache.ibatis.annotations.Param ;
import org.apache.ibatis.annotations.Select ;
public interface TeacherMapper {
@Select ( "select * from fuzaku.teacher where id = #{tid}" )
Teacher getTeacherById ( @Param ( "tid" ) int id );
}
//StudentMapper
package com.mhqdz.dao ;
public interface StudentMapper {
}
MybatisUtils工具类与最开始的相同
*Mapper.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Student
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//d.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace= "com.mhqdz.dao.StudentMapper" >
</mapper>
//Teacher
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//d.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace= "com.mhqdz.dao.TeacherMapper" >
</mapper>
Mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//d.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration:核心配置文件-->
<configuration>
<properties resource= "db.properties" />
<typeAliases>
<package name= "com.mhqdz.pojo" />
</typeAliases>
<environments default= "development" >
<environment id= "development" >
<transactionManager type= "JDBC" />
<dataSource type= "POOLED" >
<property name= "driver" value= "${driver}" />
<property name= "url" value= "${url}" />
<property name= "username" value= "${username}" />
<property name= "password" value= "${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 失败了...Could not find resource com/mhqdz/dao/*.xml-->
<!-- <!–根据后缀名批量导入–>-->
<!--<!– 一个一个来吧–>-->
<mapper class= "com.mhqdz.dao.TeacherMapper" />
<mapper class= "com.mhqdz.dao.StudentMapper" />
</mappers>
</configuration>
db.properties
1
2
3
4
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
username = root
password = zyyxy123
多对一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//StudentMapper
@Select ( "select * from fuzaku.student" )
@Results (
{
@Result ( id = true , column = "id" , property = "id" ), //id = true提示主键,@Result给学生传值
@Result ( column = "name" , property = "name" ), //column指定数据库字段的名称,property指定实体类属性的名称
//在学生库中查出tid再执行教师接口里的getTeacherById()通过tid得到teacher
@Result (
column = "tid" ,
property = "teacher" ,
javaType = Teacher . class ,
many = @Many ( select = "com.mhqdz.dao.TeacherMapper.getTeacherById" ))
}
)
List < Student > selectAllStudent ();
//TeacherMapper
@Select ( "select * from fuzaku.teacher where id = #{tid}" )
Teacher getTeacherById ( @Param ( "tid" ) int id );
一对多
错的!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Select ( "select * from fuzaku.teacher" )
@Results ({
@Result ( id = true , column = "id" , property = "id" ),
@Result ( column = "name" , property = "name" ),
@Result ( property = "students" ,
column = "tid" ,
javaType = List . class ,
many = @Many ( select = "com.mhqdz.StudentMapper.findTeacher" )
)
})
List < Teacher > getAllTeacher ();
@Select ( "select *from fuzaku.student where tid=1" ) //不管传值先放个1就不会因为传值出错,但结果无论如何都是NULL
List < Student > findTeacher ( int id );
@Result(property = "students",
column = "tid",
javaType = List.class,
many = @Many(select = "com.mhqdz.StudentMapper.findTeacher")
)
里面的值只要不空就不报错,那么是不是可以认为没给students传值的原因是这句话压根没有执行
动态sql
动态sql,本质上还是sql语句,只是我们可以在sql层面,去执行一个逻辑代码
库搭建
1
2
3
4
5
6
7
CREATE TABLE ` blog ` (
` id ` VARCHAR ( 50 ) NOT NULL COMMENT '博客id' ,
` title ` VARCHAR ( 100 ) NOT NULL COMMENT '博客标题' ,
` author ` VARCHAR ( 30 ) NOT NULL COMMENT '博客作者' ,
` create_time ` DATETIME NOT NULL COMMENT '创建时间' ,
` views ` INT ( 30 ) NOT NULL COMMENT '浏览量'
) ENGINE = INNODB DEFAULT CHARSET = utf8 ;
pojo下的Blog类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mhqdz.pojo ;
import lombok.Data ;
import java.util.Date ;
@Data
public class Blog {
private String id ;
private String title ;
private String author ;
private Date createTime ;
private int views ;
}
IdUtiles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//使用UUID()生成随机id
package com.mhqdz.utils ;
import org.junit.Test ;
import java.util.UUID ;
public class IdUtils {
public static String getId () {
return UUID . randomUUID (). toString (). replaceAll ( "-" , "" );
}
@Test
public void test (){
System . out . println ( getId ());
}
}
BlogMapper带上添加操作
1
2
3
4
5
6
7
8
9
10
11
12
<!--xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//d.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration:核心配置文件-->
<mapper namespace= "com.mhqdz.dao.blogMapper" >
<insert id= "insertBlog" parameterType= "Blog" >
insert into blog.blog(id,title,author,create_time,views)
values (#{id},#{title},#{author},#{createTime},#{views})
</insert>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
//接口文件
package com.mhqdz.dao ;
import com.mhqdz.pojo.Blog ;
import java.util.List ;
import java.util.Map ;
public interface blogMapper {
int insertBlog ( Blog blog );
}
还有几个老朋友了
if标签
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
1
2
3
4
5
6
7
8
<select id= "findActiveBlogWithTitleLike"
resultType= "Blog" >
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test= "title != null" > <!--如果title不为空 sql后面就拼接上AND title like #{title}-->
AND title like #{title}
</if>
</select>
使用if完成动态查找
1
2
//接口 使用map来传参,使用的时候 map.put("title","想查找的title")之类的赋值,然后queryBlogIf(map)
List < Blog > queryBlogIf ( Map map );
1
2
3
4
5
6
7
8
9
<select id= "queryBlogIf" parameterType= "map" resultType= "Blog" >
select *from blog.blog where 1=1 <!--where 1=1是为了方便拼接,如果不喜欢不妨看看下面的where标签-->
<if test= "title!=null" >
and title=#{title}
</if>
<if test= "author!=null" >
and author=#{author}
</if>
</select>
choose (when, otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个 使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像switch 语句。when 标签约等于case,otherwise 标签约等于default
把上面的queryBlogIf
用choose来写
1
2
3
4
5
6
7
8
9
10
11
12
13
<select id= "queryBlogChoose" parameterType= "map" resultType= "Blog" >
select *from blog.blog
<where> <!--见下-->
<choose>
<when test= "title!=null" >
and title=#{title}
</when>
<when test= "author!=null" >
and author=#{author}
</when>
</choose>
</where>
</select>
trim、where、set
更多更详细的博客
prefix:给sql语句拼接的前缀
suffix:给sql语句拼接的后缀
prefixOverrides:去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides:去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
使用where就可以把之前where 1=1
的文件改成如下形式
1
2
3
4
5
6
7
8
9
10
11
<select id= "queryBlogIf" parameterType= "map" resultType= "Blog" >
select *from blog.blog
<where>
<if test= "title!=null" >
and title=#{title}
</if>
<if test= "author!=null" >
and author=#{author}
</if>
</where>
</select>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号,
sql更新数据语句为update tableName set columnName=value where columnName=value;
使用set标签来做update
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--接口int updateBlog(Map map);-->
<!---------------------------------------------->
<update id= "updateBlog" parameterType= "map" >
update blog.blog
<set>
<if test= "title!=null" >
title=#{title},
</if>
<if test= "author!=null" >
author=#{author},
</if>
</set>
where id=#{id}
</update>
SQL片段
提取一些sql语句出来方便复用
类似别名,用include引用refid来使用sql片段
以最开始的queryBlogIf
为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<sql id= "if-title-author" >
<if test= "title!=null" >
and title=#{title}
</if>
<if test= "author!=null" >
and author=#{author}
</if>
</sql>
<select id= "queryBlogIf" parameterType= "map" resultType= "Blog" >
select *from blog.blog
<where>
<include refid= "if-title-author" />
</where>
</select>
注意:
最好基于单表定义sql片段
不要存在where标签
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历 (尤其是在构建 IN 条件语句的时候)。
1
2
3
4
5
6
7
8
9
10
11
<select id= "selectPostIn" resultType= "domain.blog.Post" >
SELECT *
FROM bolg.blog
WHERE 1=1
<foreach item= "id" index= "index" collection= "lds" //在
open= "(" separator= "or" close= ")" > //在句头写上'(' 分隔符为'or' 在句尾写上')'
#{item}
</foreach>
</select>
(id=1 or id=2 or id=3)
缓存
定义:存放在内存中的临时数据
缓存可以将数据保存在内存中,是互联网系统常常用到的。目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。缓存是在计算机内存上保存的数据,读取时无需再从磁盘读入,因此具备快速读取和使用的特点。
和大多数持久化框架一样,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 只开启一级缓存。
一级缓存
一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。
缓存失效的条件
查询不同的东西
增删改操作
查询不同的Mapper.xml
手动清理缓存(Session.clearCache();语句清理缓存)
二级缓存
二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享
一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象
二级缓存会在一级缓存提交时缓存
二级缓存的配置
开启二级缓存,默认开启但最好写上
1
2
3
<settings>
<setting name= "cacheEnabled" value= "true" />
</settings>
在Mapper文件添加<cache/>
就开启二级缓存了
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
这些属性可以通过 cache 元素的属性来修改。比如:
1
2
3
4
5
<cache
eviction= "FIFO" //使用FIFO策略
flushInterval= "60000" //每隔60秒刷新
size= "512" //最多存512个引用
readOnly= "true" /> //只读
自定义缓存(Ehcache)
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId> org.mybatis.caches</groupId>
<artifactId> mybatis-ehcache</artifactId>
<version> 1.2.1</version>
</dependency>
Spring
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
使现有的技术更容易使用,本身就是一个融合怪
Spring官网
中文文档
下载地址
GitHub
Maven
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-webmvc</artifactId>
<version> 5.3.12</version>
</dependency>
IOC
控制反转 (Inversion of Control,缩写为IoC ),是面向对象编程 中的一种设计原则,可以用来减低计算机代码 之间的耦合度 。其中最常见的方式叫做**依赖注入 (Dependency Injection,简称 DI**),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入 到对象中。
如:
有一个UserDao接口
1
2
3
4
package com.mhqdz.dao ;
public interface UserDao {
void getUser ();
}
有三个不同的实现类
在反转前 如果用户想要通过service层去调用其中的某个实现类,根据其调用的方法不同
1
2
3
4
5
6
7
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl (); //UserDaoMySQLImpl()或者SQLServerImpl(),需要用不同的对象来使用不同的类,即根据用户需求不同需要改service的源码
@Override
public void getUser () {
userDao . getUser ();
}
}
反转后
1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserServiceImpl implements UserService {
private UserDao userDao ;
//用户可以使用这个函数来改变这个接口到底对应哪个实现,不需要对service层进行改动
public void setUserDao ( UserDao userDao ){
this . userDao = userDao ;
}
@Override
public void getUser () {
userDao . getUser ();
}
}
通俗来说IOC就是把一部分的选择权交出去的思想,
HelloSpring
写一个Hello
类
1
2
3
4
5
6
7
8
9
10
11
package com.mhqdz.pojo ;
import lombok.Data ;
@Data
public class Hello {
private String name ;
public void show (){
System . out . println ( "Hello " + name );
}
}
在resources
中创建beans.xml作为Spring的文件
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!--以上都是通用的配置-->
//设置一个id为Hello的bean
<bean id= "Hello" class= "com.mhqdz.pojo.Hello" >
<property name= "name" value= "Spring" />
</bean>
</beans>
test文件
1
2
3
4
5
6
7
8
9
public class MyTest {
public static void main ( String [] args ) {
//获取Spring的上下文对象(xml文件)
ApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" );
//根据id来获取对象,因为返回Object所以要强转
Hello hello = ( Hello ) context . getBean ( "Hello" );
System . out . println ( hello . toString ());
}
}
ClassPathXmlApplicationContext
的"千层套路"……
借用Spring重写上面的UserDao的例子
beans文件
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id= "user" class= "com.mhqdz.dao.UserDaoImpl" />
<bean id= "mysql" class= "com.mhqdz.dao.UserDaoSQLServerImpl" />
<bean id= "sqlserver" class= "com.mhqdz.dao.UserDaoMySQLImpl" />
<bean id= "service" class= "com.mhqdz.service.UserServiceImpl" >
<!--ref可以引用另一个bean,此时只需要改变ref的值就可以实现调用不同实现-->
<property name= "userDao" ref= "mysql" />
</bean>
</beans>
test文件
1
2
3
4
5
6
7
public class MyTest {
public static void main ( String [] args ) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" );
UserServiceImpl userService =( UserServiceImpl ) context . getBean ( "service" );
userService . getUser ();
}
}
其他不变,需要调用不同的实现只需要改变配置文件里的配置即可
对象由Sping创建,管理,装配,这就是所谓的IOC
IOC创建对象的方式
默认使用无参构造创建对象
在执行getbean()时,即使是没有使用的bean也会创建好对象
如果要使用有参构造来创建,有三种方法
使用下标0,1,2…来一个个的赋值<constructor-arg index="0" value="java"/>
按照类型一个个的赋值<constructor-arg type="java.lang.String" value="java"/>
,如果有同类型,这种方法就会有问题,不建议使用
直接通过参数名赋值<constructor-arg name="name" value="java"/>
DI(依赖注入)
测试用class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
private String name ;
public void show (){
System . out . println ( "Hello " + name );
}
}
构造器注入
构造器注入保证一些必要的属性在Bean实例化时就得到设置,并且确保了Bean实例在实例化后就可以使用。
就是我们之前使用的方法
使用方式:
在类中,不用为属性设置setter方法,但是需要生成该类带参的构造方法。
在配置文件中配置该类的bean,并配置构造器
1
2
3
4
5
<bean id= "hello" class= "com.mhqdz.pojo.Hello" >
<constructor-arg>
<value type= "java.lang.String" > hello(构造方法注入)</value>
</constructor-arg>
</bean>
set注入
set注入指的就是在接受注入的类中定义一个要被注入的类型的一个set方法,并在参数中定义需要注入的元素。Set注入式一种装配Bean属性的直接方法,但Set注入的一个缺点就是它假设了所有的可变属性都可以通过set方法访问到,无法清晰地表示哪些属性是必须的,哪些属性是可选的
1
2
3
4
5
<bean id= "hello" class= "com.mhqdz.pojo.Hello" >
<property name= "Name" >
<value type= "java.lang.String" > hello world(set注入)</value>
</property>
</bean>
p-namespace(p-命名空间注入)
p即property,p-命名空间注入可以看成是set注入的简化形式,需要在beans.xml的头上加上xmlns:p="http://www.springframework.org/schema/p"
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
//使用p命名空间注入需要使用一下语句来导入p-命名空间xml约束
xmlns:p= "http://www.springframework.org/schema/p"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
//真正的bean语句
<bean id= "hello" class= "com.mhqdz.pojo.Hello" p:name= "hello(p注入)" />
</beans>
c-namespace(c-命名空间注入)
c即constructor-arg,c-命名空间注入可以看成是set注入的简化形式,需要在beans.xml的头上加上xmlns:c="http://www.springframework.org/schema/c"
,c-命名空间注入时也可以用c:_0="hello(c注入)"
来注入
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
//c-命名空间约束
xmlns:c= "http://www.springframework.org/schema/c"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id= "hello" class= "com.mhqdz.pojo.Hello" c:name= "hello(c注入)" /> //也可以写c:_0="hello(c注入)
</beans>
bean作用域
使用scope设置bean的作用域
scope包括:
singleton(默认):单例,指定该bean在spring容器中只有一个对象,所有通过getBean获得的对象都是同一个对象
1
<bean id= "helloService" class= "com.mhqdz.pojo.Hello" scope= "singleton" />
1
2
3
ApplicationContext context1 = new ClassPathXmlApplicationContext ( "beans.xml" );
ApplicationContext context2 = new ClassPathXmlApplicationContext ( "beans.xml" );
//context1==context2,不只有深的值相等,浅的也相等
prototype:只要重新获取该bean,都将返回一个不同的对象,prototype会浪费资源,但是并发安全
1
<bean id= "helloService" class= "com.mhqdz.pojo.Hello" scope= "prototype" />
1
2
3
ApplicationContext context1 = new ClassPathXmlApplicationContext ( "beans.xml" );
ApplicationContext context2 = new ClassPathXmlApplicationContext ( "beans.xml" );
//context1!=context2
request:在一次http请求中对应一个bean,类似于servlet
session:在一次会话中对应一个bean
request,session是web的东西,用到了再说
Spring配置
==alias==:别名,把一个名为Hello的bean取一个别名为hello,<alias name="Hello" alias="hello"/>
==bean==:用于生成对象,它的可配置量:
id:bean的唯一标识符,类似于变量名
class:bean对象所对应的全限定名(包名+类型)
name:也是别名,且可以取多个别名,可以用 ’ ’ ‘;’ ‘,’ 来分割,如同时给Hello取别名为hello和hi,<bean id="Hello" class="com.mhqdz.pojo.Hello" name="hello,hi">
abstract:设置bean是否为抽象类,默认abstract=“false”,如果设为true,将不能被实例化
autowire-candidate:默认为true,如果为false,那么该bean不能作为其他bean自动注入(自动装配)的候选者
autowire:
default(默认):即采用父级标签beans中的default-autowire属性
byName:通过属性名称来自动装配,即A类中的B对象名称为name,那么将根据id=“name"找到该bean进行装配,A类必须提供setName方法
byType:根据属性类型来找到和配置文件中配置的class类型一致的bean来自动装配,如果找到多个类型一致的bean,则抛异常,如果一个都没有找到,则不执行装配操作,也不抛出异常
no:不执行自动装配操作,只能用ref
标签进行装配
constructor:根据构造器中参数类型来自动装配,如果找到多个类型一致的bean,则抛异常,如果一个都没有找到.则不执行装配操作,但是抛出异常(这是和byType不一样的地方)
depends-on:它的作用是一个bean实例化的过程需要依赖于另一个bean的初始化,也就是说被依赖的bean将会在需要依赖的bean初始化之前加载。多个依赖bean之间用”,“号分割
destroy-method:它的作用是在销毁bean之前可以执行指定的方法,注意:必须满足scope=“singleton”,并且destroy方法参数个数不能超过1,并且参数类型只能为boolean
init-method:它的作用是在创建一个bean之后调用该方法,初始化方法必须是一个无参方法
factory-bean:指定创建bean的工厂类对象,class属性将失效
factory-method:指定创建bean的工厂方法
lazy-init:设置bean对象是否懒加载,如果设为true,则应用第一次用到bean时才实例化对象,否则在初始化spring容器时加载单例bean对象
parent:指定bean的父类,class属性失效
primary:当一个bean出现多个候选者时,设置primary=“true"后,则优先使用该bean来自动装配
scope:bean的作用范围,它包括
singleton(默认):单例,指定该bean在spring容器中只有一个对象,所有通过getBean获得的对象都是同一个对象
prototype:只要重新获取该bean,都将返回一个不同的对象
request:在一次http请求中对应一个bean,类似于servlet
session:在一次会话中对应一个bean
==import==:可以将多个配置文件导入配置进一个配置文件,如
1
2
3
<import resource= "beans.xml" />
<import resource= "beans1.xml" />
<import resource= "beans2.xml" />
Bean的自动装配
自动装配是Spring满足bean依赖的一种方式,Spring会在上下文中自动寻找,并自动给bean装配
在Spring中有3种装配方式:
在xml中配置
在java中配置
隐式的自动装配bean
测试环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//猫叫类
package com.mhqdz.pojo ;
public class Cat {
public void Jiao (){
System . out . println ( "猫猫猫" );
}
}
//狗叫类
package com.mhqdz.pojo ;
public class Dog {
public void Jiao (){
System . out . println ( "狗狗狗" );
}
}
//人的类
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
private Cat cat ;
private Dog dog ;
private String name ;
}
//测试类
import com.mhqdz.pojo.People ;
import org.springframework.context.support.ClassPathXmlApplicationContext ;
public class MyTest {
public static void main ( String [] args ) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" );
People people = context . getBean ( "People" , People . class );
people . getCat (). Jiao ();
people . getDog (). Jiao ();
System . out . println ( people . getName ());
}
}
ByName
会在容器上下文查找和自己set()方法后面的值相对应的bean id
1
2
3
4
<bean id= "cat" class= "com.mhqdz.pojo.Cat" />
<bean id= "dog" class= "com.mhqdz.pojo.Dog" />
//使用byName自动注入,使用p-命名空间注入name
<bean id= "People" class= "com.mhqdz.pojo.People" autowire= "byName" p:name= "mhqdz" />
因为Lombok自动生成的set()为public void setCat(Cat cat)
,即它将set()方法后面的值设为了cat,此时就会把id=“cat"的bean装配,dog同理
ByType
会在容器上下文查找和自己set()方法后面的值的类型相对应的bean
<bean id="dog" class="com.mhqdz.pojo.Cat"/>
<bean id="cat" class="com.mhqdz.pojo.Dog"/>
<bean id="People" class="com.mhqdz.pojo.People" autowire="byType" p:name="mhqdz"/>
set方法是public void setCat(Cat cat)
,它后面值的类型是Cat所以会去找Cat类型的bean
哪怕颠倒了cat和dog的两个id,甚至省略id也能查到
如果出现了要使用的类型有多个的情况就会报错
其他属性,Spring 配置里有简单介绍
使用注解自动装配
要使用注解必须导入约束
1
xmlns:context="http://www.springframework.org/schema/context"
启用注解
1
<context:annotation-config/>
1
2
3
4
5
6
7
8
9
10
11
12
//完整的头
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:annotation-config/>
</beans>
在需要使用的类,people类中的类上加上@Autowired
(放在set()函数上也可以)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
import org.springframework.beans.factory.annotation.Autowired ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
@Autowired
private Cat cat ;
@Autowired
private Dog dog ;
private String name ;
}
@Autowired
优先使用byType自动注入,当有多个相同类的bean时,它才会按照byName的方式注入,当使用@Autowired
时甚至set()都可以省略
与之相关的其他标签
1
2
3
4
5
@Autowired ( required = false ) 表示这个变量可以为 null ( 默认为 true 功能与 Nullable 类似 )
@Autowired
@Qualifier ( value = "dog" ) 手动选择 id = "dog" 的 bean , 如果环境比较复杂可以说使用该标签
private Dog dog ;
使用注解开发
1.在Spring4之后想要使用注解开发,必须要导aop的包 这个包,最初导入的spring-webmvc包就包括了aop包
beans.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!-- 设置开启注解-->
<context:annotation-config/>
<!-- 扫描pojo包-->
<context:component-scan base-package= "com.mhqdz.pojo" />
</beans>
被使用的类
1
2
3
4
5
6
7
8
9
10
11
package com.mhqdz.pojo ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.stereotype.Component ;
//Component(组件):这个注解等价于<bean id="user" class="com.mhqdz.pojo.User"/>
@Component
public class User {
@Value ( "mhqdz" ) //赋值注解,类似于value="mhqdz",如果有set方法,也可以放在set()上面
public String name ;
}
@Component(组件):这个注解等价于<bean id="user" class="com.mhqdz.pojo.User"/>
@Value(””) :赋值注解,类似于value=“mhqdz”,如果有set方法,也可以放在set()上面
2.Component衍生的注解
在web开发时也会按照MVC三层架构分层
dao[@Repository]
controller[@Controller]
service[@Service]
和@Component一样都是把某个类装配到Spring里,差别的意义在于区分
3.使用注解自动装配
4.作用域
类似于之前的作用域 ,在类上写注解:
1
2
3
4
5
6
7
8
9
//等价于<bean class="com.mhqdz.pojo.User" scope="singleton"/>
//@Scope("singleton")
//等价于<bean class="com.mhqdz.pojo.User" scope="prototype"/>
@Scope ( "prototype" )
public class User {
@Value ( "mhqdz" ) //赋值注解,类似于value="mhqdz",如果有set方法,也可以放在set()上面
public String name ;
}
5.小结
正题:xml好 反题:注解好 庸俗合题:xml和注解各有各的好 合题:xml好,注解只用来完成属性注入就好,xml来管理 /滑稽
xml比较万能,但相比注解不太方便
注解不是自己的类使用不了,维护复杂
JavaConfig
使用config实现配置,完全舍弃掉beans.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//作为被配置的类---User
package com.mhqdz.pojo ;
import org.springframework.beans.factory.annotation.Value ;
//说明这个类被Spring接管了,在Config文件中已经配置了@Bean所以@Component这个注解可以不写
//@Component
public class User {
String name ;
public User () {
}
public String getName () {
return this . name ;
}
//属性注入值
@Value ( "mhqdz" )
public void setName ( String name ) {
this . name = name ;
}
}
替代了beans.xml的Config文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.mhqdz.config ;
import com.mhqdz.pojo.User ;
import org.springframework.context.annotation.Bean ;
import org.springframework.context.annotation.ComponentScan ;
import org.springframework.context.annotation.Configuration ;
import org.springframework.context.annotation.Import ;
// 它本身就是一个Component
// @Configuration代表一个配置类,即之前xml里所写的beans
@Configuration
//手动扫描包,这里不写也能拿到,但万一遇到包在奇怪的位置......
@ComponentScan ( "com.mhqdz.pojo" )
//相当于引用其他beans文件,同xml里的import标签
@Import ( MyConfig2 . class )
public class MyConfig {
//注册一个bean,类似xml里的bean标签
//它的方法名getUser相当于id属性,返回值相当于clas属性
@Bean
public User getUser () {
return new User ();
}
}
test文件
1
2
3
4
5
6
7
8
9
10
11
12
13
import com.mhqdz.config.MyConfig ;
import com.mhqdz.pojo.User ;
import org.springframework.context.annotation.AnnotationConfigApplicationContext ;
import javax.swing.* ;
public class MyTest {
public static void main ( String [] args ) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext ( MyConfig . class );
User getUser = ( User ) context . getBean ( "getUser" );
System . out . println ( getUser . getName ());
}
}
代理模式
大宝贝
代理所需要的对象,代理的根本目的在于解耦,减少维护成本
代理可以在不修改原对象的基础上扩展或修改它的功能
java的常用创建代理对象的方式有三种:静态代理,基于JDK(接口)的动态代理,基于CGLIB(父类)的动态代理
抽象主题:接口或者抽象类
真实主题:被代理的真正的角色
代理:代理真实主题,纯粹的代理没有意义,代理角色一般需要做一些额外的操作
访问者:访问代理
静态代理
静态代理就是按照代理模式书写的代码,如《代理模式 》一节中的示例,其特点是代理类和目标类在代码中是确定的,因此称为静态。静态代理可以在不修改目标对象功能的前提下,对目标功能进行扩展。
静态代理模式的主要优点有:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
其主要缺点是:
代理模式会造成系统设计中类的数量增加
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package proxy ;
public class ProxyTest {
public static void main ( String [] args ) {
Proxy proxy = new Proxy ();
proxy . Request ();
}
}
//抽象主题
interface Subject {
void Request ();
}
//真实主题
class RealSubject implements Subject {
public void Request () {
System . out . println ( "访问真实主题方法..." );
}
}
//代理
class Proxy implements Subject {
private RealSubject realSubject ;
public void Request () {
if ( realSubject == null ) {
realSubject = new RealSubject ();
}
preRequest ();
realSubject . Request ();
postRequest ();
}
public void preRequest () {
System . out . println ( "访问真实主题之前的预处理。" );
}
public void postRequest () {
System . out . println ( "访问真实主题之后的后续处理。" );
}
}
程序运行的结果如下:
访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。
动态代理
动态代理分为基于JDK代理(或接口代理),基于CGLIB(父类)代理和java字节码实现有以下特点:
代理对象不需要实现接口
代理对象的生成是利用 JDK 的 API 动态的在内存中构建代理对象
能在代码运行时动态地改变某个对象的代理,并且能为代理对象动态地增加方法、增加行为
一般情况下,动态代理的底层不用我们亲自去实现,可以使用线程提供的 API 。例如,在 Java 生态中,目前普遍使用的是 JDK 自带的代理和 GGLib 提供的类库。
JDK 实现代理只需要使用 newProxyInstance 方法,该方法需要接收三个参数,语法格式如下:
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h )
注意该方法在 Proxy 类中是静态方法,且接收的三个参数说明依次为:
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,把当前执行目标对象的方法作为参数传入
例,这是一个学生为客户通过代理查找老师的例子
JDK代理
1
2
3
4
5
6
7
8
9
//接口
package com.mhqdz.service ;
public interface UserService {
void insert ();
void delete ();
void update ();
void select ();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//实现类
package com.mhqdz.service ;
public class UserServiceImpl implements UserService {
@Override
public void insert () {
System . out . println ( "一个增加的大动作给到" );
}
@Override
public void delete () {
System . out . println ( "一个删除的大动作给到" );
}
@Override
public void update () {
System . out . println ( "一个改动的大动作给到" );
}
@Override
public void select () {
System . out . println ( "一个查询的大动作给到" );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//测试类
import com.mhqdz.service.UserService ;
import com.mhqdz.service.UserServiceImpl ;
import java.lang.reflect.Proxy ;
public class MyTest {
public static void main ( String [] args ) {
//新建一个目标类对象
UserServiceImpl userService = new UserServiceImpl ();
//使用JDK的API动态生成一个代理对象
UserService service = ( UserService ) Proxy . newProxyInstance (
userService . getClass (). getClassLoader (),
userService . getClass (). getInterfaces (),
// "(proxy, method, args1) ->"这种写法是λ表达式,等价于
// new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// if ("delete".equals(method.getName())) {
// System.out.println("华丽的before");
// Object result = method.invoke(userService, args1);
// System.out.println("不平凡的after");
// return result + "嗯哼~";
// }
// Object result = method.invoke(userService, args1);
// return result;
// }
// }
// proxy就是代理类一般用不到
// method就是一个反射时获得属性的类,method.invoke(userService,args1),往里面传一个接口类,args1是参数组成的数组,
// args1是
(( proxy , method , args1 ) -> {
//可以使用method.getName()得到方法名,再通过if判断来对不同方法不同处理
if ( "delete" . equals ( method . getName ())) {
//先打印一个before,再执行对应的语句,最后打印一个after
System . out . println ( "华丽的before" ); //自带换行
Object result = method . invoke ( userService , args1 );
System . out . println ( "不平凡的after" );
//可以通过修改return来直接改变返回的形式,不过因为测试用函数返回值均为null所有没有影响
return result + "嗯哼~" ;
}
Object result = method . invoke ( userService , args1 );
return result ;
})
);
service . insert ( 11 );
System . out . println ();
service . delete ();
}
}
1
2
3
4
5
6
//运行结果
一个增加 11 的大动作给到
华丽的 before
一个删除的大动作给到
不平凡的 after
简单来说,动态代理可以看作是按照用户需要添加一些需要的条件,然后创建对应的类
稍微底层一点点Proxy.newProxyInstance
类似的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.mhqdz.service ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Method ;
public class MyNewProxyInstance implements UserService {
//InvocationHandler就是之前测试文件里的new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}}也就是((proxy, method, args1) ->)
//接收外部传递来的InvocationHandler
public InvocationHandler handler ;
//有参构造
public MyNewProxyInstance ( InvocationHandler handler ) {
this . handler = handler ;
}
@Override
public void insert ( int v ) throws Throwable {
//第一个参数是这个Method,对应的调用的方法
Method method = UserService . class . getMethod ( "insert" , int . class );
Object result = handler . invoke ( this , method , new Object []{ v });
return ;
}
@Override
public void delete () throws Throwable {
Method method = UserService . class . getMethod ( "delete" );
Object result = handler . invoke ( this , method , new Object []{});
return ;
}
@Override
public void update () throws Throwable {
Method method = UserService . class . getMethod ( "update" );
Object result = handler . invoke ( this , method , new Object []{});
return ;
}
@Override
public void select () throws Throwable {
Method method = UserService . class . getMethod ( "select" );
Object result = handler . invoke ( this , method , new Object []{});
return ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//test文件
import com.mhqdz.service.MyNewProxyInstance ;
import com.mhqdz.service.UserService ;
import com.mhqdz.service.UserServiceImpl ;
public class MyTest {
public static void main ( String [] args ) throws Throwable {
UserServiceImpl userService = new UserServiceImpl ();
UserService service =( UserService ) new MyNewProxyInstance (( proxy , method , args1 ) -> {
if ( "delete" . equals ( method . getName ())) {
System . out . println ( "华丽的before" );
Object result = method . invoke ( userService , args1 );
System . out . println ( "不平凡的after" );
return result + "嗯哼~" ;
}
Object result = method . invoke ( userService , args1 );
return result ;
});
service . insert ( 11 );
System . out . println ();
service . delete ();
}
}
==总结==:基于接口的动态代理,在反射时生成了接口的静态代理实现类
CGLIB(父类)代理
基于cglib代理,需要导入一个cglib的依赖
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId> cglib</groupId>
<artifactId> cglib</artifactId>
<version> 3.3.0</version>
</dependency>
它不要求目标类实现接口,
AOP(面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程 ,通过预编译 方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP 的延续,是软件开发中的一个热点,也是Spring 框架中的一个重要内容,是函数式编程 的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度 降低,提高程序的可重用性,同时提高了开发的效率。
简而言之,就是动态代理(Spring版)
相关名词
横切关注点:跨应用程序多个模块的方法或功能,即m与业务无关,但要用的东西,如日志,缓存,事务等
aspect(切面):横切关注点被模块化的类
advice(通知):切面必须完成的工作,即类里的方法
target(目标):被通知对象
proxy(代理):向目标对象应用通知后创建的对象
pointCut(切入点):切面通知执行的"地点"的定义
jointPoint(连接点):与切入点匹配的执行点
使用Spring实现APO
==方法1==:使用Spring API接口
1
2
3
4
5
6
7
8
9
//接口类
package com.mhqdz.service ;
public interface UserService {
void insert ();
void delete ();
void update ();
void select ();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//实现类
package com.mhqdz.service ;
public class UserServiceImpl implements UserService {
@Override
public void insert () {
System . out . println ( "一个增加的大动作给到" );
}
@Override
public void delete () {
System . out . println ( "一个删除的大动作给到" );
}
@Override
public void update () {
System . out . println ( "一个改动的大动作给到" );
}
@Override
public void select () {
System . out . println ( "一个查询的大动作给到" );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//log和afterLog,作为代理时使用的类
package com.mhqdz.log ;
import org.springframework.aop.MethodBeforeAdvice ;
import java.lang.reflect.Method ;
public class Log implements MethodBeforeAdvice {
@Override
// 要执行目标对象的方法 参数 目标对象
public void before ( Method method , Object [] args , Object target ) throws Throwable {
System . out . println ( target . getClass (). getName ()+ "的" + method . getName ()+ "了,属于是" );
}
}
package com.mhqdz.log ;
import org.springframework.aop.AfterReturningAdvice ;
import java.lang.reflect.Method ;
public class AfterLog implements AfterReturningAdvice {
@Override
//returnValue:返回值,AfterReturningAdvice是返回时写的日志,其他同log
public void afterReturning ( Object returnValue , Method method , Object [] args , Object target ) throws Throwable {
System . out . println ( "进行一个" + target . getClass (). getName ()+ "的" + method . getName ());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//beans.xml,Spring文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop= "http://www.springframework.org/schema/aop" //aop的约束
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop //aop的约束
http://www.springframework.org/schema/aop/spring-aop.xsd" > //aop的约束
<!-- 需要导入aop的约束-->
<aop:config>
<!-- expression,表达式,expression(修饰词 返回值 类名 方法名 参数)
*代表类下面的所有方法 (..)代表可以有任意个参数-->
<aop:pointcut id= "pointcut" expression= "execution(* com.mhqdz.service.UserServiceImpl.*(..)) " />
<!-- 执行环绕增加-->
<!-- 把log类切入到pointcut-->
<aop:advisor advice-ref= "log" pointcut-ref= "pointcut" />
<aop:advisor advice-ref= "afterLog" pointcut-ref= "pointcut" />
</aop:config>
<bean id= "afterLog" class= "com.mhqdz.log.AfterLog" />
<bean id= "log" class= "com.mhqdz.log.Log" />
<bean id= "userService" class= "com.mhqdz.service.UserServiceImpl" />
</beans>
关于expression="execution(* com.mhqdz.service.UserServiceImpl.*(..))
更多可以参考:Spring 之AOP AspectJ切入点语法详解
==方法2==:自定义类
1
2
3
4
5
6
7
8
9
10
11
//自定义切面
package com.mhqdz.diy ;
public class MyPointCut {
public void before (){
System . out . println ( "o^v^o" );
}
public void after (){
System . out . println ( "(>.<)" );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop= "http://www.springframework.org/schema/aop"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd" >
<!-- 自定义类-->
<bean id= "diy" class= "com.mhqdz.diy.MyPointCut" />
<aop:config>
<!--自定义切面,ref要引用的类,aspect官方名叫通知-->
<aop:aspect ref= "diy" >
<!-- 切入点-->
<aop:pointcut id= "point" expression= "execution(* com.mhqdz.service.UserServiceImpl.*(..))" />
<!-- 设置before,after执行-->
<aop:before method= "before" pointcut-ref= "point" />
<aop:after method= "after" pointcut-ref= "point" />
</aop:aspect>
</aop:config>
<bean id= "afterLog" class= "com.mhqdz.log.AfterLog" />
<bean id= "log" class= "com.mhqdz.log.Log" />
<bean id= "userService" class= "com.mhqdz.service.UserServiceImpl" />
</beans>
==方法3==:使用注解实现
详细
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//切面
package com.mhqdz.diy ;
import org.aspectj.lang.annotation.After ;
import org.aspectj.lang.annotation.Aspect ;
import org.aspectj.lang.annotation.Before ;
//标记这是一个切面
@Aspect
public class MyPointCut {
//切入点
//<aop:pointcut id="point" expression="execution(* com.mhqdz.service.UserServiceImpl.*(..))"/>
//<aop:before method="before" pointcut-ref="point"/>
//<aop:after method="after" pointcut-ref="point"/>
@Before ( "execution(* com.mhqdz.service.UserServiceImpl.*(..))" )
public void before (){
System . out . println ( "o^v^o" );
}
@After ( "execution(* com.mhqdz.service.UserServiceImpl.*(..))" )
public void after (){
System . out . println ( "(>.<)" );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop= "http://www.springframework.org/schema/aop"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd" >
//开启注解
<aop:aspectj-autoproxy/>
<bean id= "diy" class= "com.mhqdz.diy.MyPointCut" />
<bean id= "afterLog" class= "com.mhqdz.log.AfterLog" />
<bean id= "log" class= "com.mhqdz.log.Log" />
<bean id= "userService" class= "com.mhqdz.service.UserServiceImpl" />
</beans>
Mybatis-Spring
使用Spring来接管Mybatis-config.xml
文件
整合Mybatis和Spring
在Spring中创建SqlSessionFactory
以及SqlSessionTemplate(并发安全的SqlSession)
官方文档
实例:
资源
1
2
3
4
5
6
7
8
//接口类
package com.mhqdz.dao.db_library ;
import com.mhqdz.pojo.Db_library ;
import java.util.List ;
public interface Db_libraryMapper {
List < Db_library > getDb_libraryList ();
}
1
2
3
4
5
6
7
8
9
10
11
12
//Mybatis具体实现类
<?xml version="1.0" encoding="gbk" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace= "com.mhqdz.dao.db_library.Db_libraryMapper" >
<select id= "getDb_libraryList" resultType= "com.mhqdz.pojo.Db_library" >
select * from db_library.user;
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
//pojo资源类
package com.mhqdz.pojo ;
import lombok.Data ;
@Data
public class Db_library {
private String name ;
private int id ;
private int lv ;
}
实操
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//Spring文件,改改类名就可以ctrl cv
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!-- sqlSessionFactory-->
<bean id= "sqlSessionFactory" class= "org.mybatis.spring.SqlSessionFactoryBean" >
<property name= "dataSource" ref= "dataSource" />
<!--绑定Mybatis配置文件,不是必要的,在这里就可以实现,所有Mybatis的配置,但是没必要,留下点什么证明它来过~-->
<property name= "configLocation" value= "classpath:Mybatis-config.xml" />
<property name= "mapperLocations" value= "classpath:com/mhqdz/dao/db_library/*.xml" />
</bean>
<!-- dataSource:使用Spring的数据源替换Mybatis里的配置 c3p0 dbcp druid-->
<!-- 这里使用org.springframework.jdbc.datasource.DriverManagerDataSource-->
<bean id= "dataSource" class= "org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name= "driverClassName" value= "com.mysql.cj.jdbc.Driver" />
<property name= "url" value= "jdbc:mysql://localhost:3306/db_library?useSSL=true&serverTimezone=UTC" />
<property name= "username" value= "root" />
<property name= "password" value= "zyyxy123" />
</bean>
_________________________________________________________________________________
<bean id= "sqlSession" class= "org.mybatis.spring.SqlSessionTemplate" >
<!-- 只能使用constructor-arg注入因为SqlSessionTemplate没有set方法-->
<constructor-arg index= "0" ref= "sqlSessionFactory" />
</bean>
<bean id= "Db_libraryMapper" class= "com.mhqdz.dao.db_library.Db_libraryMapperImpl" >
<property name= "sqlSession" ref= "sqlSession" />
</bean>
_________________________________________________________________________________
如果Db_libraryMapperImpl实现类使用方法2:
<bean id= "Db_libraryMapper" class= "com.mhqdz.dao.db_library.Db_libraryMapperImpl" >
<property name= "sqlSessionFactory" ref= "sqlSessionFactory" />
</bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//虚有其表的config
<?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:核心配置文件-->
<configuration>
<typeAliases>
<package name= "com.mhqdz.pojo" />
</typeAliases>
<!-- 设置-->
<!-- <settings>-->
<!-- <setting name="" value=""/>-->
<!-- </settings>-->
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Db_libraryMapperImpl实现类
package com.mhqdz.dao.db_library ;
import com.mhqdz.pojo.Db_library ;
import org.mybatis.spring.SqlSessionTemplate ;
import java.util.List ;
public class Db_libraryMapperImpl implements Db_libraryMapper {
private SqlSessionTemplate sqlSession ;
public void setSqlSession ( SqlSessionTemplate sqlSession ) {
this . sqlSession = sqlSession ;
}
@Override
public List < Db_library > getDb_libraryList () {
return sqlSession . getMapper ( Db_libraryMapper . class ). getDb_libraryList ();
}
}
//------------------------------------方法2-----------------------------------------
public class Db_libraryMapperImpl extends SqlSessionDaoSupport implements Db_libraryMapper {
@Override
public List < Db_library > getDb_libraryList () {
//SqlSessionDaoSupport父类提供了getSqlSession()方法来直接获得Session
return getSqlSession (). getMapper ( Db_libraryMapper . class ). getDb_libraryList ();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Test文件
import com.mhqdz.dao.db_library.Db_libraryMapper ;
import com.mhqdz.pojo.Db_library ;
import org.junit.Test ;
import org.springframework.context.ApplicationContext ;
import org.springframework.context.support.ClassPathXmlApplicationContext ;
public class test {
@Test
public void selectTest () {
ApplicationContext context = new ClassPathXmlApplicationContext ( "Spring_Dao.xml" );
Db_libraryMapper db_libraryMapper = context . getBean ( "Db_libraryMapper" , Db_libraryMapper . class );
for ( Db_library lb : db_libraryMapper . getDb_libraryList ()){
System . out . println ( lb );
}
}
}
Spring事务管理
==声明式事务==
是AOP的应用,不影响原有代码,spring配置文件后面加一段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 配置声明式事务-->
<bean id= "transactionManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name= "dataSource" ref= "dataSource" />
</bean>
<!-- 结合AOP实现事务织入-->
<!-- 配置事务通知-->
<tx:advice id= "txAdvice" transaction-manager= "transactionManager" >
<!-- 给哪些方法配置事务-->
<tx:attributes>
<!-- <tx:method name="insert*"/>-->
<tx:method name= "*" />
<!-- <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
*代表全部方法,
REQUIRED:指定当前方法必需在事务环境中运行,如果当前有事务环境就加入当前正在执行的事务环境,如果当前没有事务,就新建一个事务 这是默认值
rollbackFor:需要回滚的异常类
-->
</tx:attributes>
</tx:advice>
<!-- 配置事务切入-->
<aop:config>
<!-- dao.*.*(..)表示dao包下的所有类的所有方法-->
<aop:pointcut id= "txPointCut" expression= "execution(* com.mhqdz.dao.*.*(..))" />
<aop:advisor advice-ref= "txAdvice" pointcut-ref= "txPointCut" />
</aop:config>
==编程式事务==
在需要执行的函数上套一个try…catch,如果失败了就回滚
SpringMVC
即Spring+MVC
MVC:
Controller:控制器
取得表单数据
调用业务逻辑
转向指定的页面
Model:模型
业务逻辑
保存数据的状态
View:视图
显示页面
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
SpringMVC官方文档
Spring MVC的特点:
轻量级,简单易学
高效 , 基于请求响应的MVC框架
与Spring兼容性好,无缝结合
约定优于配置
功能强大:RESTful、数据验证、格式化、本地化、主题等
简洁灵活
最重要的一点还是用的人多 , 使用的公司多 .
Hello SpringMVC
配置版
使用配置完成第一个SpringMVC程序,配置版并不实用,仅做学习原理时使用
1.新建一个Moudle,添加web的支持
2.确定导入了SpringMVC 的依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<dependencies>
<dependency>
<groupId> junit</groupId>
<artifactId> junit</artifactId>
<version> 4.13.2</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-webmvc</artifactId>
<version> 5.3.12</version>
</dependency>
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> servlet-api</artifactId>
<version> 2.5</version>
</dependency>
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> servlet-api</artifactId>
<version> 2.5</version>
</dependency>
<dependency>
<groupId> javax.servlet.jsp</groupId>
<artifactId> jsp-api</artifactId>
<version> 2.2</version>
</dependency>
<dependency>
<groupId> javax.servlet.jsp</groupId>
<artifactId> jsp-api</artifactId>
<version> 2.2</version>
</dependency>
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> jstl</artifactId>
<version> 1.2</version>
</dependency>
</dependencies>
3.配置web.xml,注册DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns= "http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version= "4.0" >
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name> springmvc</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--关联springmvc的配置文件-->
<init-param>
<param-name> contextConfigLocation</param-name>
<param-value> classpath:SpringMVC-servlet.xml</param-value>
</init-param>
<!--启动级别-1,即随服务器启动-->
<load-on-startup> 1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name> springmvc</servlet-name>
<url-pattern> /</url-pattern>
</servlet-mapping>
</web-app>
4.编写SpringMVC(Spring)配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!-- 处理映射器-->
<bean class= "org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 处理器适配器-->
<bean class= "org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- 视图解析器,定义一个路径从中拿到网页-->
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" id= "InternalResourceViewResolver" >
<!-- 前缀-->
<property name= "prefix" value= "/WEB-INF/jsp/" />
<!-- 后缀-->
<property name= "suffix" value= ".jsp" />
</bean>
<!-- 注册实现类-->
<bean id= "/hello" class= "com.mhqdz.controller.HelloController" />
</beans>
5.编写操作业务Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.mhqdz.controller ;
import org.springframework.web.servlet.ModelAndView ;
import org.springframework.web.servlet.mvc.Controller ;
import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse ;
//导入Controller接口
public class HelloController implements Controller {
public ModelAndView handleRequest ( HttpServletRequest request , HttpServletResponse response ) throws Exception {
//新建一个ModelAndView 模型和视图
ModelAndView mv = new ModelAndView ();
//放一个值到mv里
mv . addObject ( "msg" , "HelloSpringMVC!" );
//放入转跳页面的名字,实际地址会加上Spring配置文件里的前后缀:/WEB-INF/jsp/hello.jsp
mv . setViewName ( "hello" );
return mv ;
}
}
6.编写hello.jsp要转跳去的网页
1
2
3
4
5
6
7
8
9
< %@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title> mhqdz</title>
</head>
<body>
${msg}
</body>
</html>
注解版
只有Controller不一样(毕竟只有Controller是java文件……)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//HelloController
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.RequestMapping ;
@Controller //Controller标记它是控制器
//@RequestMapping("/HelloController")
public class HelloController {
@RequestMapping ( "/h1" ) //网页上的地址
public String hello ( Model model ){
//向hello.jsp中添加属性msg与值
model . addAttribute ( "msg" , "hello,SpringMVC" );
//打开hello,拼上Spring文件中设置的前后缀后的全名 web-inf/jsp/hello.jsp
return "hello" ;
}
}
RestFul 风格
概念
Restful就是一个资源定位及资源操作的风格 不是标准也不是协议,只是一种风格 基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制
计算a+b的值,为此需要传入a+b
如下是未使用Restful风格的程序,此时想要把a,b传入需要用’?‘来传值http://localhost:8080/h1?a=1&b=2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.RequestMapping ;
@Controller
public class HelloController {
@RequestMapping ( "/h1" )
public String hello ( int a , int b , Model model ){
model . addAttribute ( "msg" , "结果为" +( a + b ));
return "hello" ;
}
}
如下是使用了Restful风格的程序
只需要这么传就可以了http://localhost:8080/h1/1/2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.PathVariable ;
import org.springframework.web.bind.annotation.RequestMapping ;
@Controller
public class HelloController {
//在路径中写两个占位符用'/'来传值,非必需是'/'
@RequestMapping ( "/h1/{a}/{b}" )
//@PathVariable:接收请求路径中占位符的值
public String hello ( @PathVariable int a , @PathVariable int b , Model model ){
model . addAttribute ( "msg" , "结果为" +( a + b ));
return "hello" ;
}
}
method属性
@RequestMapping
中可以添加method属性,以指定请求类型,请求类型不对会报405的错误 指定请求谓词的类型如GET POST HEAD OPTIONS PUT PATCH DELETE TRACE
等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.PathVariable ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMethod ;
@Controller
public class HelloController {
//method = {RequestMethod.GET}只能使用GET请求来获得
//可以简写为@GetMapping("/h1/{a}+{b}")
@RequestMapping ( value = "/h1/{a}+{b}" , method = { RequestMethod . GET })
public String hello ( @PathVariable int a , @PathVariable int b , Model model ){
model . addAttribute ( "msg" , "结果为" +( a + b ));
return "hello" ;
}
}
类似于简写@GetMapping,@RequestMapping根据method值的不同,有如下几个变体:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
数据处理及跳转
转跳
视图解析器会为return的结果添上头尾,但不会给redirect:
添,用redirect:目标地址
就可以实现转跳
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.* ;
@Controller
public class HelloController {
@RequestMapping ( "/h1" )
public String hello ( Model model ){
return "redirect:/index.jsp" ;
}
}
数据处理
把数据拿到后台为所欲为即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.web.bind.annotation.* ;
@Controller
public class HelloController {
@RequestMapping ( "/h1" )
//@RequestParam("username"):username是网页传值时的参数名称,如果网页上用的参数名和后台的名字相同 可以不写
//http://localhost:8080/h1?username=mhqdz
public String hello ( @RequestParam ( "username" ) String name ){
System . out . println ( name );
return "hello" ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//RestFul风格
package com.mhqdz.controller ;
import org.springframework.stereotype.Controller ;
import org.springframework.web.bind.annotation.* ;
@Controller
public class HelloController {
@RequestMapping ( "/h1/{name}" )
//http://localhost:8080/h1/mhqdz
public String hello ( @PathVariable String name ){
System . out . println ( name );
return "hello" ;
}
}
当需要传入的值为一个对象时,会调用函数对象的构造方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//对象
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
int id ;
int age ;
String name ;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mhqdz.controller ;
import com.mhqdz.pojo.User ;
import org.springframework.stereotype.Controller ;
import org.springframework.web.bind.annotation.* ;
@Controller
public class HelloController {
@GetMapping ( "/h1" )
public String hello ( User user ){
System . out . println ( user . toString ());
return "hello" ;
}
}
将数据给前端
第一种 : 通过ModelAndView
最初使用的方法
1
2
3
4
5
6
7
8
9
10
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest ( HttpServletRequest httpServletRequest , HttpServletResponse httpServletResponse ) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView ();
mv . addObject ( "msg" , "ControllerTest1" );
mv . setViewName ( "test" );
return mv ;
}
}
第二种 : 通过Model
后面的几个例子中常用的方法,是ModelMap的阉割版,常用的功能都具备了
1
2
3
4
5
6
7
8
@RequestMapping ( "/ct2/hello" )
public String hello ( @RequestParam ( "username" ) String name , Model model ){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model . addAttribute ( "msg" , name );
System . out . println ( name );
return "test" ;
}
第三种 : 通过ModelMap
ModeMap就相当于是Model的完整版,继承了 LinkedMap,除了实现了自身的一些方法,同样的继承LinkedMap的方法和特性
1
2
3
4
5
6
7
8
@RequestMapping ( "/hello" )
public String hello ( @RequestParam ( "username" ) String name , ModelMap model ){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model . addAttribute ( "name" , name );
System . out . println ( name );
return "hello" ;
}
乱码过滤器
在web.xml上加上这个,SpringMVC的乱码过滤器,没了
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name> encoding</filter-name>
<filter-class> org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name> encoding</param-name>
<param-value> utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name> encoding</filter-name>
<url-pattern> /*</url-pattern>
</filter-mapping>
Json
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛。
采用完全独立于编程语言的文本格式 来存储和表示数据。
简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在 JavaScript 语言中,一切都是对象。因此,任何JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。看看他的要求和语法格式:
对象表示为键值对,数据由逗号分隔
花括号保存对象
方括号保存数组
Json乱码
在SpringMVC-config.xml
中加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--防止JSON乱码,死代码,只要用了jackSon就把它复制上去-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults= "true" >
<bean class= "org.springframework.http.converter.StringHttpMessageConverter" >
<constructor-arg value= "UTF-8" />
</bean>
<bean class= "org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
<property name= "objectMapper" >
<bean class= "org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" >
<property name= "failOnEmptyBeans" value= "false" />
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
JackSon
常用的转json工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.mhqdz.controller ;
import com.fasterxml.jackson.core.JsonProcessingException ;
import com.fasterxml.jackson.databind.ObjectMapper ;
import com.fasterxml.jackson.databind.SerializationFeature ;
import com.mhqdz.pojo.User ;
import org.springframework.stereotype.Controller ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.ResponseBody ;
import org.springframework.web.bind.annotation.RestController ;
import java.text.SimpleDateFormat ;
import java.util.Arrays ;
import java.util.Date ;
import java.util.List ;
//调用视图解析器
//@Controller
@RestController //所有返回值不走视图解析器,直接返回字符串
public class userController {
//@ResponseBody//返回值不走视图解析器,直接返回字符串
//produces属性可以指定响应体返回类型和编码
@RequestMapping ( "/j1" )
public String json1 () throws JsonProcessingException {
User user = new User ( 1 , "满怀期待中" );
ObjectMapper mapper = new ObjectMapper ();
return mapper . writeValueAsString ( user );
}
//返回数组
@RequestMapping ( "/j2" )
public String json2 () throws JsonProcessingException {
User user1 = new User ( 1 , "满怀期待中" );
User user2 = new User ( 2 , "灭火器道中" );
User user3 = new User ( 3 , "妹红祈祷中" );
List < User > user = Arrays . asList ( new User []{ user1 , user2 , user3 });
return new ObjectMapper (). writeValueAsString ( user );
}
//返回时间
@RequestMapping ( "/j3" )
public String json3 () throws JsonProcessingException {
//定义格式,时间戳转换为人能看得懂的东西
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-mm-dd hh:mm:ss" );
return new ObjectMapper (). writeValueAsString ( sdf . format ( new Date ()));
------------------------------ 方法 2 ---------------------------------------
ObjectMapper mapper = new ObjectMapper ();
mapper . configure ( SerializationFeature . WRITE_DATES_AS_TIMESTAMPS , false ); //设置不使用时间戳
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-mm-dd hh:mm:ss" );
mapper . setDateFormat ( sdf );
return mapper . writeValueAsString ( new Date ());
}
}
1
2
3
4
5
//要复用的话直接调用返回就能省好多代码,很多源码也是这么做的,但是读的时候会有“子子孙孙无穷匮也”的感觉呢(笑)
@RequestMapping ( "/j4" )
public String json4 () throws JsonProcessingException {
return json3 ();
}
SSM整合项目——图书管理系统
1.pom.xml依赖,以及静态资源过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<dependencies>
<!--Junit-->
<dependency>
<groupId> junit</groupId>
<artifactId> junit</artifactId>
<version> 4.13.2</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId> mysql</groupId>
<artifactId> mysql-connector-java</artifactId>
<version> 8.0.27</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId> com.mchange</groupId>
<artifactId> c3p0</artifactId>
<version> 0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> servlet-api</artifactId>
<version> 2.5</version>
</dependency>
<dependency>
<groupId> javax.servlet.jsp</groupId>
<artifactId> jsp-api</artifactId>
<version> 2.2</version>
</dependency>
<dependency>
<groupId> javax.servlet</groupId>
<artifactId> jstl</artifactId>
<version> 1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId> org.mybatis</groupId>
<artifactId> mybatis</artifactId>
<version> 3.5.7</version>
</dependency>
<dependency>
<groupId> org.mybatis</groupId>
<artifactId> mybatis-spring</artifactId>
<version> 2.0.3</version>
</dependency>
<!--Spring-->
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-webmvc</artifactId>
<version> 5.3.12</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-jdbc</artifactId>
<version> 5.3.12</version>
</dependency>
<dependency>
<groupId> org.projectlombok</groupId>
<artifactId> lombok</artifactId>
<version> 1.18.22</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory> src/main/java</directory>
<includes>
<include> **/*.properties</include>
<include> **/*.xml</include>
</includes>
<filtering> false</filtering>
</resource>
<resource>
<directory> src/main/resources</directory>
<includes>
<include> **/*.properties</include>
<include> **/*.xml</include>
</includes>
<filtering> false</filtering>
</resource>
</resources>
</build>
2.建立基本结构和配置框架
3.按照框架填文件
Mybatis层
1.数据库配置文件:database.properties
1
2
3
4
5
#在头上加上"jdbc."是因为username会被解析为电脑的username 排了一个小时错 觉了
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
jdbc.username = root
jdbc.password = zyyxy123
3.编写MyBatis的核心配置文件:mybatis-config.xml
稍微写点东西证明MyBatis来过
1
2
3
4
5
6
7
8
9
10
11
12
<?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>
<typeAliases>
<package name= "com.mhqdz.pojo" />
</typeAliases>
<mappers>
<mapper resource= "com/mhqdz/mapper/BookMapper.xml" />
</mappers>
</configuration>
4.编写pojo文件下的实体类:Books
使用了lombok
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mhqdz.pojo ;
import lombok.AllArgsConstructor ;
import lombok.Data ;
import lombok.NoArgsConstructor ;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int bookID ;
private String bookName ;
private int bookCounts ;
private String detail ;
}
5.编写Dao(Mapper)层
Mapper接口:BookMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.mhqdz.mapper ;
import com.mhqdz.pojo.Books ;
import java.util.List ;
public interface BookMapper {
//增加一个Book
int addBook ( Books book );
//根据id删除一个Book
int deleteBookById ( int id );
//更新Book
int updateBook ( Books books );
//根据id查询,返回一个Book
Books queryBookById ( int id );
//根据Name查询,返回一个Book
Books queryBookByName ( String Name );
//查询全部Book,返回list集合
List < Books > queryAllBook ();
}
Mapper接口借助MyBatis实现:BookMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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= "com.mhqdz.mapper.BookMapper" >
<!--增加一个Book-->
<insert id= "addBook" parameterType= "Books" >
insert into ssmbuild.books(bookName,bookCounts,detail)
values (#{bookName}, #{bookCounts}, #{detail})
</insert>
<!--根据id删除一个Book-->
<delete id= "deleteBookById" parameterType= "int" >
delete from ssmbuild.books where bookID=#{bookID}
</delete>
<!--更新Book-->
<update id= "updateBook" parameterType= "Books" >
update ssmbuild.books
set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}
where bookID = #{bookID}
</update>
<!--根据id查询,返回一个Book-->
<select id= "queryBookById" resultType= "Books" >
select * from ssmbuild.books
where bookID = #{bookID}
</select>
<!--根据Name查询,返回一个Book-->
<select id= "queryBookByName" resultType= "Books" >
select * from ssmbuild.books
where bookName = #{bookName}
</select>
<!--查询全部Book-->
<select id= "queryAllBook" resultType= "Books" >
SELECT * from ssmbuild.books
</select>
</mapper>
6.编写Service层
接口:BookService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.mhqdz.service ;
import com.mhqdz.pojo.Books ;
import java.util.List ;
public interface BookService {
//增加一个Book
int addBook ( Books book );
//根据id删除一个Book
int deleteBookById ( int id );
//更新Book
int updateBook ( Books books );
//根据id查询,返回一个Book
Books queryBookById ( int id );
//根据Name查询,返回一个Book
Books queryBookByName ( String Name );
//查询全部Book,返回list集合
List < Books > queryAllBook ();
}
实现(指向mapper):BookServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.mhqdz.service ;
import com.mhqdz.mapper.BookMapper ;
import com.mhqdz.pojo.Books ;
import java.util.List ;
public class BookServiceImpl implements BookService {
//调用dao层的操作,设置一个set接口,方便Spring管理
private BookMapper bookMapper ;
public void setBookMapper ( BookMapper bookMapper ) {
this . bookMapper = bookMapper ;
}
public int addBook ( Books book ) {
return bookMapper . addBook ( book );
}
public int deleteBookById ( int id ) {
return bookMapper . deleteBookById ( id );
}
public int updateBook ( Books books ) {
return bookMapper . updateBook ( books );
}
public Books queryBookById ( int id ) {
return bookMapper . queryBookById ( id );
}
public Books queryBookByName ( String Name ){
return bookMapper . queryBookByName ( Name );
}
public List < Books > queryAllBook () {
return bookMapper . queryAllBook ();
}
}
Spring层
注意:所有的spring支文件都要通过==File->Project Structure->Moudules==整合到一块
或者在applicationContext.xml中import所有的文件:
1
2
3
< import resource = "classpath:SpringMapper.xml" />
< import resource = "classpath:SpringService.xml" />
< import resource = "classpath:SpringMVC-config.xml" />
1.整合一些MyBatis配置:SpringMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd" >
<!-- 配置整合mybatis -->
<!-- 1.关联数据库文件 -->
<context:property-placeholder location= "classpath:database.properties" />
<!-- 2.数据库连接池 -->
<!--数据库连接池
dbcp 半自动化操作 不能自动连接
c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
-->
<bean id= "dataSource" class= "com.mchange.v2.c3p0.ComboPooledDataSource" >
<!-- 配置连接池属性 -->
<property name= "driverClass" value= "${jdbc.driver}" />
<property name= "jdbcUrl" value= "${jdbc.url}" />
<property name= "user" value= "${jdbc.username}" />
<property name= "password" value= "${jdbc.password}" />
<!-- 以下属性都是默认的,不需要设置的话可以不写-->
<!-- c3p0连接池的私有属性 -->
<property name= "maxPoolSize" value= "30" />
<property name= "minPoolSize" value= "10" />
<!-- 关闭连接后不自动commit -->
<property name= "autoCommitOnClose" value= "false" />
<!-- 获取连接超时时间 -->
<property name= "checkoutTimeout" value= "10000" />
<!-- 当获取连接失败重试次数 -->
<property name= "acquireRetryAttempts" value= "2" />
</bean>
<!-- 3.配置SqlSessionFactory对象 -->
<bean id= "sqlSessionFactory" class= "org.mybatis.spring.SqlSessionFactoryBean" >
<!-- 注入数据库连接池 -->
<property name= "dataSource" ref= "dataSource" />
<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
<property name= "configLocation" value= "classpath:mybatis-config.xml" />
</bean>
<!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
<!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
<bean class= "org.mybatis.spring.mapper.MapperScannerConfigurer" >
<!-- 注入sqlSessionFactory -->
<property name= "sqlSessionFactoryBeanName" value= "sqlSessionFactory" />
<!-- 给出需要扫描Dao接口包 -->
<property name= "basePackage" value= "com.mhqdz.mapper" />
</bean>
</beans>
2.整合service:SpringService.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!-- 扫描service相关的包 -->
<context:component-scan base-package= "com.mhqdz.service" />
<!--BookServiceImpl注入到IOC容器中-->
<bean id= "BookServiceImpl" class= "com.mhqdz.service.BookServiceImpl" >
<property name= "bookMapper" ref= "bookMapper" />
</bean>
<!-- 配置事务管理器 -->
<bean id= "transactionManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<!-- 注入数据库连接池 -->
<property name= "dataSource" ref= "dataSource" />
</bean>
</beans>
SpringMVC层
右键项目添加web依赖
web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns= "http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version= "4.0" >
<!--DispatcherServlet-->
<servlet>
<servlet-name> DispatcherServlet</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name> contextConfigLocation</param-name>
<!--这里加载的是总配置文件-->
<param-value> classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup> 1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name> DispatcherServlet</servlet-name>
<url-pattern> /</url-pattern>
</servlet-mapping>
<!--防乱码-->
<filter>
<filter-name> encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name> encoding</param-name>
<param-value> utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name> encodingFilter</filter-name>
<url-pattern> /*</url-pattern>
</filter-mapping>
<!--Session过期时间-->
<session-config>
<session-timeout> 15</session-timeout>
</session-config>
</web-app>
spring-mvc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context= "http://www.springframework.org/schema/context"
xmlns:mvc= "http://www.springframework.org/schema/mvc"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd" >
<!-- 配置SpringMVC -->
<!-- 1.开启SpringMVC注解驱动 -->
<mvc:annotation-driven />
<!-- 2.静态资源默认servlet配置-->
<mvc:default-servlet-handler/>
<!-- 3.配置jsp 显示ViewResolver视图解析器 -->
<bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name= "viewClass" value= "org.springframework.web.servlet.view.JstlView" />
<property name= "prefix" value= "/WEB-INF/jsp/" />
<property name= "suffix" value= ".jsp" />
</bean>
<!-- 4.扫描web相关的bean -->
<context:component-scan base-package= "com.mhqdz.controller" />
</beans>
applicationContext.xml整合
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:context= "http://www.springframework.org/schema/p"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<import resource= "classpath:SpringMapper.xml" />
<import resource= "classpath:SpringService.xml" />
<import resource= "classpath:SpringMVC-config.xml" />
</beans>
jsp前端页面以及转跳实现的后端代码
index首页
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML>
<html>
<head>
<title>首页</title>
<style type="text/css">
a {
/*去掉下划线*/
text-decoration: none;
color: black;
font-size: 18px;
}
h3 {
width: 180px;
height: 38px;
margin: 100px auto;
text-align: center;
line-height: 38px;
background: deepskyblue;
border-radius: 4px;
}
</style>
</head>
<body>
<h3>
<a href="${pageContext.request.contextPath}/book/allBook">点击进入列表页</a>
</h3>
</body>
</html>
在WEB-INF->jsp下的以post访问的三个jsp文件
addBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增书籍</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>新增书籍</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
书籍名称:<input type="text" name="bookName"><br><br><br>
书籍数量:<input type="text" name="bookCounts"><br><br><br>
书籍详情:<input type="text" name="detail"><br><br><br>
<input type="submit" value="添加">
</form>
</div>
allBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍列表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<%-- small:小型文本--%>
<small>书籍列表 —— 显示所有书籍</small>
</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a>
</div>
<div class="col-md-8 column" style="float: right">
<form class="form-inline" action="${pageContext.request.contextPath}/book/queryByName" method="post">
<span style="color: red;">${error}</span>
<input type="text" name="queryByName" placeholder="请输入需要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名字</th>
<th>书籍数量</th>
<th>书籍详情</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="book" items="${requestScope.get('list')}">
<tr>
<td>${book.getBookID()}</td>
<td>${book.getBookName()}</td>
<td>${book.getBookCounts()}</td>
<td>${book.getDetail()}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
<a href="${pageContext.request.contextPath}/book/del/${book.getBookID()}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
updateBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改信息</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>修改信息</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<input type="hidden" name="bookID" value="${book.getBookID()}"/>
书籍名称:<input type="text" name="bookName" value="${book.getBookName()}"/>
书籍数量:<input type="text" name="bookCounts" value="${book.getBookCounts()}"/>
书籍详情:<input type="text" name="detail" value="${book.getDetail() }"/>
<input type="submit" value="提交"/>
</form>
</div>
后端代码实现在Mybatis层的BookController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.mhqdz.controller ;
import com.mhqdz.pojo.Books ;
import com.mhqdz.service.BookService ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.beans.factory.annotation.Qualifier ;
import org.springframework.stereotype.Controller ;
import org.springframework.ui.Model ;
import org.springframework.web.bind.annotation.PathVariable ;
import org.springframework.web.bind.annotation.RequestMapping ;
import java.util.ArrayList ;
import java.util.List ;
@Controller
@RequestMapping ( "/book" )
public class BookController {
@Autowired
@Qualifier ( "BookServiceImpl" )
private BookService bookService ;
@RequestMapping ( "/allBook" )
public String list ( Model model ) {
List < Books > list = bookService . queryAllBook ();
model . addAttribute ( "list" , list );
return "allBook" ;
}
//跳到addBook的页面
//需要先采集需要添加书的信息,所以需要这一步
//下面update同
@RequestMapping ( "/toAddBook" )
public String toAddPaper () {
return "addBook" ;
}
//执行addBook
@RequestMapping ( "/addBook" )
public String addPaper ( Books books ) {
System . out . println ( books );
bookService . addBook ( books );
//执行后返回首页
return "redirect:/book/allBook" ;
}
@RequestMapping ( "/toUpdateBook" )
public String toUpdateBook ( Model model , int id ) {
Books books = bookService . queryBookById ( id );
System . out . println ( books );
model . addAttribute ( "book" , books );
return "updateBook" ;
}
@RequestMapping ( "/updateBook" )
public String updateBook ( Model model , Books book ) {
System . out . println ( book );
bookService . updateBook ( book );
//提交事务,不过它自己会自动提交
// Books books = bookService.queryBookById(book.getBookID());
// model.addAttribute("books", books);
return "redirect:/book/allBook" ;
}
@RequestMapping ( "/del/{bookId}" )
public String deleteBook ( @PathVariable ( "bookId" ) int bookId ) {
bookService . deleteBookById ( bookId );
return "redirect:/book/allBook" ;
}
@RequestMapping ( "/queryByName" )
public String queryByName ( Model model , String queryByName ) {
if ( queryByName . equals ( "" )){
return "redirect:/book/allBook" ;
}
Books books = bookService . queryBookByName ( queryByName );
if ( books == null ){
model . addAttribute ( "error" , "查不到" );
}
System . out . println ( books );
List < Books > list = new ArrayList <>();
list . add ( books );
model . addAttribute ( "list" , list );
return "allBook" ;
}
}
到这里就完成了,如果需要新的功能在最后的jsp前端页面以及转跳实现的后端代码
里加就行了
最后完整的结构图