1. 创建数据库
1.创建toutiao数据库。
2.创建需要的表。现在只创建User,News表。
User | Conmment | Message | News |
---|---|---|---|
id | id | id | id |
name | content | formiid | title |
password | userid | toid | link |
salt | created_date | content | image |
head_url | news_id | conversation_id | like_count |
created_date | comment_count | ||
user_id | |||
created_date |
2. 项目里连接数据库
1.配置环境:
在上一章搭建的项目里继续,加入依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.配置数据库链接,放在资源下的application.properties里。在资源里加入配置文件mybatis-config.xml。
application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/toutiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123
mybatis.config-location=classpath:mybatis-config.xml
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>
<settings>
<!-- Globally enables or disables any caches configured in any mapper under this configuration -->
<setting name="cacheEnabled" value="true"/>
<!-- Sets the number of seconds the driver will wait for a response from the database -->
<setting name="defaultStatementTimeout" value="3000"/>
<!-- Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names aColumn -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- Allows JDBC support for generated keys. A compatible driver is required.
This setting forces generated keys to be used if set to true,
as some drivers deny compatibility but still work -->
<setting name="useGeneratedKeys" value="true"/>
</settings>
<!-- Continue going here -->
</configuration>
3. 基于注解配置
3.1 定义与表对应的类
在model里创建News.class和User.class。
News.class:
package com.nowcoder.toutiao.model;
import java.util.Date;
public class News {
private int id;
private String title;
private String link;
private String image;
private int likeCount;
private int commentCount;
private int userId;
private Date createdDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public int getLikeCount() {
return likeCount;
}
public void setLikeCount(int likeCount) {
this.likeCount = likeCount;
}
public int getCommentCount() {
return commentCount;
}
public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
}
User.class:
package com.nowcoder.toutiao.model;
public class User {
private int id;
private String name;
private String password;
private String salt;
private String headUrl;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getHeadUrl() {
return headUrl;
}
public void setHeadUrl(String headUrl) {
this.headUrl = headUrl;
}
}
3.2 建立dao包
dao(持久层)包内是与数据库进行交互的程序。(controller(web层)是与网页,service(服务层/业务层)是dao和controller中间那层)
1.创建UserDAO.class
package com.nowcoder.toutiao.dao;
import com.nowcoder.toutiao.model.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
// 一个简单的插入
@Mapper // 表示这个接口是与数据库一一匹配的
public interface UserDAO {
// 减少代码的重复写;(注意空格,不要少空格)
String TABLE_NAME = "user";
String INSERT_FIELD = " name, password, salt, head_url ";
String SELECT_FEILDS = " id, name, password, salt, head_url ";
// 通过注解实现的
// @Insert({"insert into user(name,password,salt,head_url)values()"})
@Insert({"insert into ",TABLE_NAME ,"(", INSERT_FIELD, ")" +
"values(#{name}, #{password},#{salt}, #{headUrl})"})
int addUser(User user);
}
2.写一个测试用例
InitDatabaseTest.class:
package com.nowcoder.toutiao;
import com.nowcoder.toutiao.dao.UserDAO;
import com.nowcoder.toutiao.model.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.Random;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ToutiaoApplication.class)
@Sql("/init-schema.sql") //创建文件中的表
public class InitDatabaseTests {
@Autowired
UserDAO userDAO;
@Test
public void contextLoads() {
Random random = new Random();
for (int i=0;i<11;i++){
User user = new User();
user.setName(String.format("USER%d",i));
user.setPassword("");
user.setSalt("");
user.setHeadUrl(String.format("http://images.nowcoder.com/head/%dt.png",random.nextInt(1000)));
userDAO.addUser(user);
}
}
}
这里可能报错:
(1)第二章的IndexController里有用到User,但是这里User改变了,所以把它里面用到的注释掉。
(2)出现mysql相关的错误,password那个。是因为它用的老版本的5.x.x,和要连接的数据库的版本不同,我用的8.0.17的,把依赖里的版本改为8.0.17 。
(2019.8.2新的)还是数据库的问题。
org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: Access denied for user ‘root’@‘localhost’ (using password: YES)
之前出现这个问题,改了依赖版本。还是不行,想起上次查了别人说的改数据库的权限。但是数据库的权限其实是有的,不用改,改了也没用。。然后上次最后解决办法是重新安装了数据库。。。这次还没有改 换了数据库,装了一个5.6的版本MYSQL,按照这个安装新的MYSQL:添加链接描述
因为安装的时候MYSQL端口号设置为了3307,在application.properties里的端口号要记得改。然后就运行成功了。
3.在UserDAO里继续添加其他方法:select,update,delete。
package com.nowcoder.toutiao.dao;
import com.nowcoder.toutiao.model.User;
import org.apache.ibatis.annotations.*;
@Mapper // 表示这个接口食欲数据库一一匹配的
public interface UserDAO {
// 减少代码的重复写;(注意空格,不要少空格)
String TABLE_NAME = "user";
String INSERT_FIELD = " name, password, salt, head_url ";
String SELECT_FEILDS = " id, name, password, salt, head_url ";
// 通过注解
// @Insert({"insert into user(name,password,salt,head_url)values()"})
@Insert({"insert into ",TABLE_NAME ,"(", INSERT_FIELD, ")" +
"values(#{name}, #{password},#{salt}, #{headUrl})"})
int addUser(User user);
@Select({"select ", SELECT_FEILDS, " from ",TABLE_NAME, " where id=#{id}"})
User selectById(int id);
@Update({"update ",TABLE_NAME, " set password =#{password} where id=#{id}"})
void updatePassword(User user);
@Delete({"delete from ", TABLE_NAME, " where id=#{id}"})
void deleteById(int id);
}
测试:
package com.nowcoder.toutiao;
import com.nowcoder.toutiao.dao.UserDAO;
import com.nowcoder.toutiao.model.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Random;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ToutiaoApplication.class)
@Sql("/init-schema.sql") //创建文件中的表
public class InitDatabaseTests {
@Autowired
UserDAO userDAO;
@Test
public void initTest() {
Random random = new Random();
for (int i=0;i<11;i++){
User user = new User();
user.setName(String.format("USER%d",i));
user.setPassword("");
user.setSalt("");
user.setHeadUrl(String.format("http://images.nowcoder.com/head/%dt.png",random.nextInt(1000)));
userDAO.addUser(user);
user.setPassword("new password");
userDAO.updatePassword(user);
}
Assert.assertEquals("new password",userDAO.selectById(1).getPassword());
userDAO.deleteById(1);
Assert.assertNull(userDAO.selectById(1));
}
}
4.创建UserDAO.class。用基于xml的方式。
用xml写的话可以写一些比较复杂的逻辑操作,用注解的话使用比较简单。
package com.nowcoder.toutiao.dao;
import com.nowcoder.toutiao.model.News;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface NewsDAO {
String TABLE_NAME = "news";
String INSERT_FIELD = " title, link, image, like_count, comment_count, user_id, created_date ";
String SELECT_FEILDS = " id, title, link, image, like_count, comment_count, user_id, created_date ";
// 通过注解配置:
@Insert({"insert ", " into ", TABLE_NAME, "(", INSERT_FIELD,") " +
"values(#{title}, #{link}, #{image}, " +
"#{likeCount}, #{commentCount}, #{userId}, #{createdDate})"})
int addNews(News news);
//通过xml配置:(xml配置:放在resource的相同包目录下定义的同名xml)
List<News> selectByUserIdAndOffset(@Param("userId") int userId,
@Param("offset") int offset,
@Param("limit") int limit);
}
注意:在resource目录下创建相同包目录时,要用new directory一个一个建,比如说先建com路径,再在里面建nowcoder路径。如果一次性输入com.nowcoder.xxxx.xxx,这时建立的路径上只有一个文件夹,该文件夹名字就是com.nowcoder.xxxx.xxx,而不是我们想要的包含子文件的com文件夹。
创建xml配置文件:NewsDAO.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.nowcoder.toutiao.dao.NewsDAO">
<sql id="table">news</sql>
<sql id="selectFields">id,title, link, image, like_count, comment_count,created_date,user_id
</sql>
<select id="selectByUserIdAndOffset" resultType="com.nowcoder.toutiao.model.News">
SELECT
<include refid="selectFields"/>
FROM
<include refid="table"/>
<if test="userId != 0">
WHERE user_id = #{userId}
</if>
ORDER BY id DESC
LIMIT #{offset},#{limit}
</select>
</mapper>
测试:
package com.nowcoder.toutiao;
import com.nowcoder.toutiao.dao.NewsDAO;
import com.nowcoder.toutiao.dao.UserDAO;
import com.nowcoder.toutiao.model.News;
import com.nowcoder.toutiao.model.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.xml.crypto.Data;
import java.util.Date;
import java.util.Random;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ToutiaoApplication.class)
@Sql("/init-schema.sql") //创建文件中的表
public class InitDatabaseTests {
@Autowired
UserDAO userDAO;
@Autowired
NewsDAO newsDAO;
@Test
public void initDataTest() {
Random random = new Random();
for (int i=0;i<11;i++){
User user = new User();
user.setName(String.format("USER%d",i));
user.setPassword("");
user.setSalt("");
user.setHeadUrl(String.format("http://images.nowcoder.com/head/%dt.png",random.nextInt(1000)));
userDAO.addUser(user);
News news = new News();
news.setTitle(String.format("TITLE:{%d}",i));
news.setLink(String.format("http://www.nowcoder.com/%d.html",i));
news.setImage(String.format("http://images.nowcoder.com/head/%dm.png",random.nextInt(1000)));
news.setLikeCount(i+1);
news.setCommentCount(i);
news.setUserId(i+1);
Date date = new Date();
date.setTime(date.getTime()+1000*3600*i*5);
news.setCreatedDate(date);
newsDAO.addNews(news);
user.setPassword("new password");
userDAO.updatePassword(user);
}
Assert.assertEquals("new password",userDAO.selectById(1).getPassword());
userDAO.deleteById(1);
Assert.assertNull(userDAO.selectById(1));
}
}
3.3 创建service类
创建一个UserService.class:(为了在controller可以调用该服务)
package com.nowcoder.toutiao.service;
import com.nowcoder.toutiao.dao.UserDAO;
import com.nowcoder.toutiao.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.jws.soap.SOAPBinding;
@Service
public class UserService {
@Autowired
private UserDAO userDAO;
public User getUser(int id){
return userDAO.selectById(id);
};
}
创建一个NewsService.class:
package com.nowcoder.toutiao.dao;
import com.nowcoder.toutiao.model.News;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface NewsDAO {
String TABLE_NAME = "news";
String INSERT_FIELD = " title, link, image, like_count, comment_count, user_id, created_date ";
String SELECT_FEILDS = " id, title, link, image, like_count, comment_count, user_id, created_date ";
// 通过注解配置:
@Insert({"insert ", " into ", TABLE_NAME, "(", INSERT_FIELD,") " +
"values(#{title}, #{link}, #{image}, " +
"#{likeCount}, #{commentCount}, #{userId}, #{createdDate})"})
int addNews(News news);
//通过xml配置:(xml配置:放在resource的相同包目录下定义的同名xml)
List<News> selectByUserIdAndOffset(@Param("userId") int userId,
@Param("offset") int offset,
@Param("limit") int limit);
}
3.4 建立主页
3.4.1 在controller建立HomeCotroller.class:
package com.nowcoder.toutiao.controller;
import com.nowcoder.toutiao.service.NewsService;
import com.nowcoder.toutiao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpSession;
@Controller
public class HomeController {
@Autowired
NewsService newsService;
@Autowired
UserService userService;
@RequestMapping(path={"/","/home"},method = {RequestMethod.GET, RequestMethod.GET})
public String home(HttpSession session){
return "home";
}
}
3.4.2 把home.html放到resource的templates下。把其他文件放进statics下。
3.4.3 修改application.properties:
加入了强制用html类型
spring.datasource.url=jdbc:mysql://localhost:3306/toutiao?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123
mybatis.config-location=classpath:mybatis-config.xml
#logging.level.root=DEBUG
spring.velocity.suffix=.html
spring.velocity.cache=false
#spring.velocity.toolbox-config-location=toolbox.xml
3.4.4 运行ToutiaoApplication里的main函数,出错:
下面的没截了。
原因时该端口被占用了?参考:
https://blog.csdn.net/zmq52007/article/details/81332450
更改端口。在application.properties里加入server.port=(新的端口号,比如8090)
此时再运行函数,没有报错。
3.4.5 再访问该网页
网页只有头部和底部的,中间是loading。(如果出现没有home.html资源,那就clean一下再运行:)
出现
2019-08-09 15:39:53.568 ERROR 8804 --- [nio-8090-exec-2] org.apache.velocity : ResourceManager : unable to find resource 'error.html' in any resource loader.
因为此时还没写完(= =b)。继续写homecontroller。
3.4.6 homecontroller:
package com.nowcoder.toutiao.controller;
import com.nowcoder.toutiao.model.News;
import com.nowcoder.toutiao.model.User;
import com.nowcoder.toutiao.model.ViewObject;
import com.nowcoder.toutiao.service.NewsService;
import com.nowcoder.toutiao.service.UserService;
import org.apache.catalina.LifecycleState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
@Controller
public class HomeController {
@Autowired
NewsService newsService;
@Autowired
UserService userService;
@RequestMapping(path={"/","/home"},method = {RequestMethod.GET, RequestMethod.GET})
public String home(Model model){
// 把所有的资讯拷过来
List<News> newsList = newsService.getLatestNews(0,0,10);
List<ViewObject> vos = new ArrayList<>();
for (News news:newsList){
ViewObject vo = new ViewObject();
vo.set("news",news);
vo.set("user",userService.getUser(news.getUserId()));
vos.add(vo);
}
model.addAttribute("vos", vos);
return "home";
}
}
3.4.7 使用ViewObject/DateTool
ViewObject:方便传递任何数据到Velocity,也是一个model。
DateTool:Velocity自带工具类导入
导入ViewObject.class:
package com.nowcoder.toutiao.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Created by rainday on 16/6/30.
*/
public class ViewObject {
private Map<String, Object> objs = new HashMap<String, Object>();
public void set(String key, Object value) {
objs.put(key, value);
}
public Object get(String key) {
return objs.get(key);
}
}
导入toolbox:
<toolbox>
<tool>
<key>date</key>
<scope>application</scope>
<class>org.apache.velocity.tools.generic.DateTool</class>
</tool>
</toolbox>
修改application.properties,加入:
spring.velocity.toolbox-config-location=toolbox.xml
3.4.8 修改home.html
俺也不懂,俺什么都不知道。直接复制他的代码,不知道怎么用的。
这时再运行主页,中间就有内容了,只是有些地方还不对,因为后面还要继续修改。
1.运行主页,出现错误:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;
在网上搜的方法:
(1)model类全都实现Serializable接口,但是依然报这个错。x
(2)自己把home.html删了后重加。。然后报新错了:
但是这个不影响功能,不管他。
(3)参考:https://blog.csdn.net/lgq2016/article/details/89094281
发现在pom里少加了依赖。
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-tools</artifactId>
</dependency>
加入后,运行主页成功。