Spring+Mybatis基於註解整合Redis

基於這段時間折騰redis遇到了各種問題,想着整理一下。本文主要介紹基於Spring+Mybatis以註解的形式整合Redis。廢話少說,進入正題。

  首先準備Redis,我下的是Windows版,下載後直接啓動redis-server就行了,見下圖:

  

一,先上jar包

  

二,創建實體類     

複製代碼
 1 package com.sl.user.vo;
 2 
 3 import java.io.Serializable;
 4 
 5 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 6 import com.fasterxml.jackson.databind.annotation.JsonNaming;
 7 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 8 
 9 @JsonSerialize  
10 @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)  
11 public class UserVO implements Serializable{
12 
13     private static final long serialVersionUID = 1L;
14     
15     private int id;
16     private String username;
17     private String password;
18     private int age;
19     
20     public UserVO(){
21         super(); 
22     }
23     
24     public UserVO(int id, String username, String password, int age) {
25         super();
26         this.id = id;
27         this.username = username;
28         this.password = password;
29         this.age = age;
30     }
31 
32     public int getId() {
33         return id;
34     }
35 
36     public void setId(int id) {
37         this.id = id;
38     }
39 
40     public String getUsername() {
41         return username;
42     }
43 
44     public void setUsername(String username) {
45         this.username = username;
46     }
47 
48     public String getPassword() {
49         return password;
50     }
51 
52     public void setPassword(String password) {
53         this.password = password;
54     }
55 
56     public int getAge() {
57         return age;
58     }
59 
60     public void setAge(int age) {
61         this.age = age;
62     }
63 
64     @Override
65     public String toString() {
66         return "UserVO [id=" + id + ", username=" + username + ", password="
67                 + password + ", age=" + age + "]";
68     }
69 
70 }
複製代碼

三,dao接口

複製代碼
 1 package com.sl.user.dao;
 2 
 3 import com.sl.user.vo.UserVO;
 4 
 5 public interface UserDao {
 6     
 7     public void addUser(UserVO user);
 8     
 9     public void deleteUser(UserVO user);
10     
11     public void updateUser(UserVO user);
12     
13     public UserVO getUserById(int id);
14     
15     public UserVO getUser(int id);
16     
17 }
複製代碼

四,UserMapper

複製代碼
 1 <?xml version="1.0" encoding="UTF-8" ?>  
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
 3 <mapper namespace="com.sl.user.dao.UserDao" >
 4     
 5     <resultMap id="userResult" type="User">
 6         <result column="id" property="id"/>
 7         <result column="userame" property="userame"/>
 8         <result column="password" property="password"/>
 9         <result column="age" property="age"/>
10     </resultMap>
11     
12     <insert id="addUser" parameterType="User">
13         insert into t_user(username,password,age) values(#{username},#{password},#{age})
14     </insert>
15     
16     <update id="deleteUser" parameterType="User">
17         delete * from t_user where id = #{id}
18     </update>
19     
20     <update id="updateUser" parameterType="User">
21         update t_user set
22         <if test="username != null and username != ''"> username = #{username},</if>
23         <if test="password != null and password != ''"> password = #{password},</if>
24         <if test="age != null and age != ''"> age = #{age}</if>
25         where 1=1
26         <if test="id != null and id != ''">and id = #{id}</if>
27         
28     </update>
29     
30     <select id="getUser" parameterType="int" resultType="User" >
31         select * from t_user where id = #{id}
32     </select>
33     
34     <select id="getUserById" parameterType="int" resultType="java.lang.String" >
35         select username from t_user where id = #{id}
36     </select>
37     
38 </mapper>  
複製代碼

五,Service接口

複製代碼
 1 package com.sl.user.service;
 2 
 3 import com.sl.user.vo.UserVO;
 4 
 5     public interface UserService {
 6     
 7         public void addUser(UserVO user);
 8         
 9         public void deleteUser(UserVO user);
10         
11         public void updateUser(UserVO user);
12         
13         public UserVO getUserById(int id);
14         
15         public UserVO getUser(int id);
16     
17 }
複製代碼

六,Service實現

複製代碼
 1 package com.sl.user.service.impl;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.cache.annotation.CacheEvict;
 5 import org.springframework.cache.annotation.Cacheable;
 6 import org.springframework.stereotype.Service;
 7 import org.springframework.transaction.annotation.Propagation;
 8 import org.springframework.transaction.annotation.Transactional;
 9 
10 import com.sl.user.dao.UserDao;
11 import com.sl.user.service.UserService;
12 import com.sl.user.vo.UserVO;
13 
14 @Service("userService")
15 @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) 
16 public class UserServiceImpl implements UserService{
17     
18     @Autowired
19     private UserDao userDao;
20     
21     @Override
22     @CacheEvict(value="User",key="addUser",allEntries=true) 
23     public void addUser(UserVO user) {
24         userDao.addUser(user);
25     }
26 
27     @Override
28     @CacheEvict(value = {"getUser", "getUserById"}, allEntries = true)  
29     public void deleteUser(UserVO user) {
30         userDao.deleteUser(user);
31     }
32 
33     @Override
34     @CacheEvict(value = {"getUser", "getUserById"}, allEntries = true)  
35     public void updateUser(UserVO user) {
36         userDao.updateUser(user);
37     }
38 
39     @Override
40     @Cacheable(value="User",key="getUserById")
41     public UserVO getUserById(int id) {
42         return userDao.getUserById(id);
43     }
44     
45     @Override
46     @Cacheable(value="User",key="'getUser'")
47     public UserVO getUser(int id) {
48         return userDao.getUser(id);
49     }
50 
51 }
複製代碼

七,Ctrl層

複製代碼
 1 package com.sl.user.web;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.stereotype.Controller;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.ResponseBody;
10 
11 import com.sl.user.service.UserService;
12 import com.sl.user.vo.UserVO;
13 
14 @Controller
15 @RequestMapping("/userCtrl")
16 public class UserCtrl {
17     
18     @Autowired
19     private UserService userService;
20     
21     @RequestMapping("/addUser")
22     public void addUser(UserVO user){
23         userService.addUser(user);
24     }
25     
26     @RequestMapping("/deleteUser")
27     public void deleteUser(UserVO user){
28         userService.deleteUser(user);
29     }
30     
31     @RequestMapping("/updateUser")
32     public void updateUser(UserVO user){
33         userService.updateUser(user);
34     }
35     
36     @ResponseBody
37     @RequestMapping("/getUserById")
38     public Map<String,Object> getUserById(UserVO user){
39         Map<String,Object> map = new HashMap<String,Object>();
40         map.put("msg",userService.getUserById(4));
41         return map;
42     }
43     
44     @ResponseBody
45     @RequestMapping("/getUser")
46     public Map<String,Object> getUser(UserVO vo){
47         Map<String,Object> map = new HashMap<String,Object>();
48         Object user = userService.getUser(4);
49         map.put("msg",user.toString());
50         return map;
51     }
52     
53 }
複製代碼

八,Redis關鍵類,用於CRUD操作

複製代碼
  1 package com.sl.user.redis;
  2 import java.io.ByteArrayInputStream;  
  3 import java.io.ByteArrayOutputStream;  
  4 import java.io.IOException;  
  5 import java.io.ObjectInputStream;  
  6 import java.io.ObjectOutputStream;  
  7   
  8 import org.springframework.cache.Cache;  
  9 import org.springframework.cache.support.SimpleValueWrapper;  
 10 import org.springframework.dao.DataAccessException;  
 11 import org.springframework.data.redis.connection.RedisConnection;  
 12 import org.springframework.data.redis.core.RedisCallback;  
 13 import org.springframework.data.redis.core.RedisTemplate;
 14 
 15 public class RedisUtil implements Cache{
 16     
 17     private RedisTemplate<String, Object> redisTemplate;    
 18     private String name;    
 19     public RedisTemplate<String, Object> getRedisTemplate() {  
 20         return redisTemplate;    
 21     }  
 22        
 23     public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {  
 24         this.redisTemplate = redisTemplate;    
 25     }  
 26        
 27     public void setName(String name) {  
 28         this.name = name;    
 29     }  
 30        
 31     @Override    
 32     public String getName() {  
 33         return this.name;    
 34     }  
 35   
 36     @Override    
 37     public Object getNativeCache() {  
 38         return this.redisTemplate;    
 39     }  
 40     
 41     /**
 42      * 從緩存中獲取key
 43      */
 44     @Override    
 45     public ValueWrapper get(Object key) {  
 46 System.out.println("get key");  
 47       final String keyf =  key.toString();  
 48       Object object = null;  
 49       object = redisTemplate.execute(new RedisCallback<Object>() {  
 50       public Object doInRedis(RedisConnection connection)    
 51                   throws DataAccessException {  
 52           byte[] key = keyf.getBytes();  
 53           byte[] value = connection.get(key);  
 54           if (value == null) {  
 55              return null;  
 56             }  
 57           return toObject(value);  
 58           }  
 59        });  
 60         return (object != null ? new SimpleValueWrapper(object) : null);  
 61       }  
 62     
 63      /**
 64       * 將一個新的key保存到緩存中
 65       * 先拿到需要緩存key名稱和對象,然後將其轉成ByteArray
 66       */
 67      @Override    
 68      public void put(Object key, Object value) {  
 69 System.out.println("put key");  
 70        final String keyf = key.toString();    
 71        final Object valuef = value;    
 72        final long liveTime = 86400;    
 73        redisTemplate.execute(new RedisCallback<Long>() {    
 74            public Long doInRedis(RedisConnection connection)    
 75                    throws DataAccessException {    
 76                 byte[] keyb = keyf.getBytes();    
 77                 byte[] valueb = toByteArray(valuef);    
 78                 connection.set(keyb, valueb);    
 79                 if (liveTime > 0) {    
 80                     connection.expire(keyb, liveTime);    
 81                  }    
 82                 return 1L;    
 83              }    
 84          });    
 85       }  
 86   
 87       private byte[] toByteArray(Object obj) {    
 88          byte[] bytes = null;    
 89          ByteArrayOutputStream bos = new ByteArrayOutputStream();    
 90          try {    
 91            ObjectOutputStream oos = new ObjectOutputStream(bos);    
 92            oos.writeObject(obj);    
 93            oos.flush();    
 94            bytes = bos.toByteArray();    
 95            oos.close();    
 96            bos.close();    
 97           }catch (IOException ex) {    
 98                ex.printStackTrace();    
 99           }    
100           return bytes;    
101         }    
102   
103        private Object toObject(byte[] bytes) {  
104          Object obj = null;    
105            try {  
106                ByteArrayInputStream bis = new ByteArrayInputStream(bytes);    
107                ObjectInputStream ois = new ObjectInputStream(bis);    
108                obj = ois.readObject();    
109                ois.close();    
110                bis.close();    
111            } catch (IOException ex) {    
112                ex.printStackTrace();    
113             } catch (ClassNotFoundException ex) {    
114                ex.printStackTrace();    
115             }    
116             return obj;    
117         }  
118        
119        /**
120         * 刪除key
121         */
122        @Override    
123        public void evict(Object key) {    
124 System.out.println("del key");  
125          final String keyf = key.toString();    
126          redisTemplate.execute(new RedisCallback<Long>() {    
127          public Long doInRedis(RedisConnection connection)    
128                    throws DataAccessException {    
129              return connection.del(keyf.getBytes());    
130             }    
131           });    
132         }  
133        
134         /**
135          * 清空key
136          */
137         @Override    
138         public void clear() {    
139 System.out.println("clear key");  
140            redisTemplate.execute(new RedisCallback<String>() {    
141                 public String doInRedis(RedisConnection connection)    
142                         throws DataAccessException {    
143                   connection.flushDb();    
144                     return "ok";    
145                }    
146            });    
147         }  
148   
149         @Override  
150         public <T> T get(Object key, Class<T> type) {  
151             return null;  
152         }  
153       
154         @Override  
155         public ValueWrapper putIfAbsent(Object key, Object value) {  
156             return null;  
157         }  
158       
159 }
複製代碼

九,Spring整合mybatis和redis配置文件

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:p="http://www.springframework.org/schema/p"
 5     xmlns:context="http://www.springframework.org/schema/context" 
 6     xmlns:tx="http://www.springframework.org/schema/tx"
 7     xmlns:mvc="http://www.springframework.org/schema/mvc"
 8     xmlns:aop="http://www.springframework.org/schema/aop"
 9     xmlns:cache="http://www.springframework.org/schema/cache"
10     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
11        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
12        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd  
13        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
14        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
15        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
16     
17     <!-- 掃描dao,service -->
18     <context:component-scan base-package="com.sl.user.service" />
19     <context:component-scan base-package="com.sl.user.service.*" />
20     <context:component-scan base-package="com.sl.user.redis" />
21     <!-- 啓用註解 -->
22     <context:annotation-config/>
23     <!-- 啓動緩存註解 -->
24     <cache:annotation-driven/>
25     
26     <!-- MyBatis start -->
27         <!-- 配置dataSource DriverManagerDataSource-->
28         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
29             <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
30             <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
31             <property name="username" value="root"></property>
32             <property name="password" value="root"></property>
33         </bean>
34     
35         <!-- MyBatis配置 SqlSessionFactoryBean -->
36         <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
37             <property name="dataSource" ref="dataSource"></property>
38             <property name="configLocation" value="classpath:config/mybatis.xml"></property>
39             <property name="mapperLocations" value="classpath:mapper/UserMapper.xml"></property>
40         </bean>
41         
42         <!-- mybatis自動掃描加載Sql映射文件/接口 : MapperScannerConfigurer 
43                    sqlSessionFactory
44                 basePackage:指定sql映射文件/接口所在的包(自動掃描) -->
45         <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
46             <property name="sqlSessionFactory" ref="sessionFactory"></property>
47             <property name="basePackage" value="com.sl.user.dao"></property>
48         </bean>
49                 
50          <!-- 事務管理  DataSourceTransactionManager-->
51          <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
52              <property name="dataSource" ref="dataSource"></property>
53          </bean>
54          
55          <!-- 使用聲明式事務 transaction-manager:引用上面定義的事務管理器-->
56          <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
57          
58      <!-- MyBatis end -->
59      
60      <!-- 配置redis部分 start -->
61      
62          <!-- 配置redis連接池  JedisPoolConfig-->
63          <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
64              <property name="maxIdle" value="300" />  
65             <property name="maxTotal" value="600" />  
66          </bean> 
67          
68          <!-- 配置CoonnectionFactory JedisConnectionFactory-->
69          <bean id="connFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
70               <property name="hostName" value="127.0.0.1"></property>
71               <property name="port" value="6379"></property>
72               <property name="poolConfig" ref="poolConfig"></property>
73          </bean>
74         
75          <!-- 配置redisTemplate StringRedisTemplate-->
76          <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
77             <property name="connectionFactory" ref="connFactory"/>
78          </bean>
79          
80          <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">    
81              <property name="caches">    
82                 <set>    
83                     <bean class="com.sl.user.redis.RedisUtil">    
84                          <property name="redisTemplate" ref="redisTemplate" />    
85                          <property name="name" value="User"/>    
86                          <!-- User名稱要在類或方法的註解中使用 -->  
87                     </bean>  
88                 </set>    
89              </property>    
90          </bean>
91 
92 </beans>
複製代碼

十,SpringMVC配置文件

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 3     xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
 6        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
 7        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd  
 8        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
 9        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
10 
11     <mvc:annotation-driven/>
12     <!-- 啓用spring mvc 註解 -->
13     <context:annotation-config/>
14     <!-- 設置使用註解的類所在的jar包 -->
15     <context:component-scan base-package="com.sl.user.*"></context:component-scan>
16 
17     <!-- 對模型視圖名稱的解析,即在模型視圖名稱添加前後綴 -->
18     <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
19         <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
20         <property name="prefix" value="/views/"/>
21         <property name="suffix" value=".jsp"/>
22     </bean>
23 
24     <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
25         <!-- JSON轉換器 -->
26         <property name="messageConverters">
27             <list>
28                 <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
29                     <property name="supportedMediaTypes">
30                         <list>
31                             <value>application/json;charset=utf-8</value>
32                             <value>text/json;charset=utf-8</value>
33                         </list>
34                     </property>
35                 </bean>
36             </list>
37         </property>
38     </bean>
39 
40 </beans>
複製代碼

十一,mybatis配置文件

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
 3 "http://mybatis.org/dtd/mybatis-3-config.dtd">
 4 <configuration>
 5 
 6     <!-- 實體類,簡稱 -設置別名 -->
 7     <typeAliases>
 8         <typeAlias alias="User" type="com.sl.user.vo.UserVO" />
 9     </typeAliases>
10     
11 </configuration>  
複製代碼

十二,log4j

複製代碼
 1 # Set root category priority to INFO and its only appender to CONSOLE.
 2 log4j.rootCategory=DEBUG, CONSOLE
 3 #log4j.rootCategory=INFO, CONSOLE, LOGFILE
 4 
 5 # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
 6 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
 7 log4j.appender.CONSOLE.Threshold=DEBUG
 8 log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
 9 log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p - %m%n
10 
11 log4j.logger.java.sql.Connection=DEBUG
12 log4j.logger.java.sql.Statement=DEBUG
13 log4j.logger.java.sql.PreparedStatement=DEBUG
14 log4j.logger.java.sql.ResultSet=DEBUG
複製代碼

十三,web.xml

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app version="3.0" 
 3     xmlns="http://java.sun.com/xml/ns/javaee" 
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 6     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
 7   <display-name>TestRedis</display-name>
 8   
 9   <context-param>
10         <param-name>contextConfigLocation</param-name>
11         <param-value>
12             classpath:config/applicationContext.xml
13         </param-value>
14     </context-param>
15     
16     <context-param>
17         <param-name>log4jConfigLocation</param-name>
18         <param-value>classpath:config/log4j.properties</param-value>
19     </context-param>
20     <context-param>
21         <param-name>log4jRefreshInterval</param-name>
22         <param-value>60000</param-value>
23     </context-param>
24     
25     <listener>
26         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
27     </listener>
28     <!-- 日誌 -->
29     <listener>
30         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
31     </listener>
32     
33     <servlet>
34         <servlet-name>spring</servlet-name>
35         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
36         <init-param>
37             <param-name>contextConfigLocation</param-name>
38             <param-value>classpath:config/SpringMVC.xml</param-value>
39         </init-param>
40         <load-on-startup>1</load-on-startup>
41     </servlet>
42     <servlet-mapping>
43         <servlet-name>spring</servlet-name>
44         <url-pattern>*.do</url-pattern>
45     </servlet-mapping>
46     
47     <!-- 解決中文亂碼問題 -->
48     <filter>
49         <filter-name>characterEncoding</filter-name>
50         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
51         <init-param>
52             <param-name>encoding</param-name>
53             <param-value>UTF-8</param-value>
54         </init-param>
55         <init-param>
56             <param-name>forceEncoding</param-name>
57             <param-value>true</param-value>
58         </init-param>
59     </filter>
60     <filter-mapping>
61         <filter-name>characterEncoding</filter-name>
62         <url-pattern>*.do</url-pattern>
63     </filter-mapping>
64   
65       
66   <welcome-file-list>
67     <welcome-file>index.jsp</welcome-file>
68   </welcome-file-list>
69 </web-app>
複製代碼

十四,測試,已查詢爲例(getUser()方法),jsp測試頁面整的比較醜就不貼出來了,自己寫一個吧。。。

查詢前:

執行第一次查詢:

執行第二次查詢操作:

 

上圖可見,沒有再執行sql,直接從redis中獲取數據。

 

大功告成!!!!

在下初學,文中多有不足之處,還望多多指教。不喜勿噴。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章