Node-web05-02博客系统数据显示

博客系统数据显示

代码

在Next.js里 创建 connection

  • lib/getDatabaseConnection.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import {createConnection} from 'typeorm';

const promise = (async function () {
console.log('创建connection.')
/*
立即执行函数,引用的时候被执行,始终返回 promise的结果
1。 createConnection 是一个 返回promise的 所以想要他必须包裹在 async 的匿名函数里 代号xx
2。 然后立即执行 xx
3。 promise 这个函数就代表 我去执行 createConnection 这个异步操作
*/
return await createConnection();
})();


export const getDatabaseConnection = async () => {
/*
别人引用的时候 第一次得到 connection
第二次依然得到 connection,因为 promise只执行了一次
promise 之后不管重复引入多少次都不会重新执行
*/
return promise;
};

pages/index.tsx里

1
2
3
4
5
6
7
8
9
10
export const getServerSideProps: GetServerSideProps = async (context) => {
const connect = await getDatabaseConnection()
console.log('connect');
const ua = context.req.headers['user-agent'];
return {
props: {
browser: ua
}
};
};
  • 遇到问题了 如果是第一次运行yarn dev 这里连接是没问题的
  • 但是如果中途 修改代码 ctrl+s 后 会导致热启动,然后重新执行 getDatabaseConnection() 但是我们的连接已经存在过一次了。所以会报错
  • 这个问题是 next.js 本地开发 修改文件自动加载导致的
  • 只会出现在开发环境,就是你本地yarn dev 然后修改代码的时候触发
1
2
Server Error
AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.

解决 connection 开发环境修改代码重复执行问题

  • 使用 getConnectionManager

  • 修改lib/getDatabaseConnection.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {createConnection,getConnectionManager} from 'typeorm';

const promise = (async function () {
const manager = getConnectionManager();
if (!manager.has('default')) {
return createConnection();
} else {
const current = manager.get('default');
if(current.isConnected){
return current
}else{
return createConnection();
}
}
})();

export const getDatabaseConnection = async () => {
return promise;
};

疑问:我们能不能自己写一个 manager

  • 可以,但是会有一个问题
  • 我们的代码是在项目里,一旦引入,如果在开发环境下,还是会存在重复执行的问题
  • 而 typeorm 的 manager为啥没有被重新执行,因为他在 node_modules 里,nextjs发现 如果你的文件是 node_modules 里的就不会去刷新

复述这个问题:开发中遇到最难的bug

  • 一开始我们在 nextjs里 直接 getConnection 会报错,因为 还没初始化过
  • 我们用 createConnecion,为了防止执行多次
  • 我们把它放在 立即执行函数里返回,保证多次 import 都始终执行一次
  • 尝试 create + get 判空的方式
    • 又遇到问题 ctrl+s 修改代码导致 重新加载 每次都触发 create,重复了
  • 最后我们使用typeorm的 getConnectionManager 来管理
    • 因为 typeorm 的代码 处于 node_modules里,nextjs修改代码保存的时候 不会重新加载
  • 自己写 manager 没用,因为 nextjs 里 ctrl+s 热重启导致 你写的 manager 重新加载重新执行。即又触发了 create

获取Post内容

  • 前提你通过 node dist/seed.js 创造好数据
  • pages/index.tsx里获取 posts
1
2
3
4
5
6
7
8
9
10
11
12
13
export const getServerSideProps:GetServerSideProps = async (context)=>{
const connection = await getDatabaseConnection()// 第一次链接能不能用 get
const posts = await connection.manager.find(Post)

console.log('posts');
console.log(posts)
return {
props: {
aaa:'aaadaa'
}
}

}
  • 报错 No metadata for "Post" was found.
  • 原因是 connection 是换成 js之后执行的,他不知道这个 post 有那些列
    • 为啥 seed.js知道,因为他引入了 reflect-metadata
    • reflect-metadata 可以通过你引入的 entity 类,反向推出它的元数据
    • 而我们 nextjs 里没有引入它
  • createConnecion,构造的时候可以传递参数,告知它的 entity和 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
26
27
28
29
30
31
32
33
import {createConnection,getConnectionManager} from 'typeorm';
import 'reflect-metadata';
import {Post} from 'src/entity/Post';
import {User} from 'src/entity/User';
import {Comment} from 'src/entity/Comment';
import config from 'ormconfig.json';

const create = async () => {
// @ts-ignore
return createConnection({
...config,
entities: [Post, User, Comment]
});
};

const promise = (async function () {
const manager = getConnectionManager();
if (!manager.has('default')) {
return create();
} else {
const current = manager.get('default');
if(current.isConnected){
return current
}else{
return create();
}
}
})();


export const getDatabaseConnection = async () => {
return promise;
};

BUG:no metadata for Post 再次出现

  • 我们展示 posts的时候,ctrl+s 导致一个问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise = (async function () {
const manager = getConnectionManager();
if (!manager.has('default')) {
return create();
} else {
const current = manager.get('default');
if(current.isConnected){
// ctrl+s会走进这里,如果走进这里 no metadata for Post 就会触发
return current
}else{
return create();
}
}
})();
  • 修改,current.isConnected 就关闭连接,再次 create()
1
2
3
4
5
6
const promise = (async function () {
const manager = getConnectionManager();
const current = manager.has('default') && manager.get('default');
if (current) {await current.close();}
return create()
})();