ent orm笔记2---schema使用(上)
在上一篇关于快速使用ent orm的笔记中,我们再最开始使用entc init User
创建schema,在ent orm 中的schema 其实就是数据库模型,在schema中我们可以通过Fields 定义数据库中表的字段信息;通过Edges 定义表之间的关系信息;通过Index 定义字段的索引信息等等,这篇文章会整理一下关于ent orm 中如何使用这些。
备注:文章中的所有代码在github.com/peanut-cc/ent_orm_notes
Fileds
当我们执行 entc init User
之后,会在当前目录下生成一个ent目录,在该目录下有一个schema目录,默认情况下schema/user.go文件如下:
package schema
import "github.com/facebook/ent"
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return nil
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return nil
}
如果要对user 这表添加字段,需要在Fileds方法中添加如下所示:
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("username").
Unique(),
field.Time("created_at").
Default(time.Now),
field.Float32("salary").
Optional(),
}
}
注意: 默认情况下,所有字段都是必填字段,可以使用Optional方法将其设置为optional。
数据类型
下面的数据类型都是支持的:
- All Go numeric types. Like int, uint8, float64, etc.
- bool
- string
- time.Time
- []byte (only supported by SQL dialects).
- JSON (only supported by SQL dialects).
- Enum (only supported by SQL dialects).
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age"),
field.String("username").
Unique(),
field.Time("created_at").
Default(time.Now),
field.Float32("salary").
Optional(),
field.Bool("active").
Default(false),
field.JSON("strings", []string{}).
Optional(),
field.Enum("state").
Values("on", "off").
Optional(),
}
}
ID字段
数据库表的id字段,默认是内置的,不需要单独添加,其类型默认为int, 并在数据库中自动递增,
为了将id配置为在所有表中唯一,需要在schema migration的时候使用WithGlobalUniqueID
如果需要对id字段进行其他配置,或者想要使用UUID格式存id,则需要覆盖id的配置。
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("id").
StructTag(`json:"oid,omitempty"`),
}
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.UUID("id", uuid.UUID{}),
}
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("id").
MaxLen(25).
NotEmpty().
Unique().
Immutable(),
}
}
数据库类型
每个数据库都有自己的从go的数据类型到数据库类型的映射,例如,Mysql 在数据库中将float64字段创建为双精度的。ent orm 有一个选项参数可以使用SchemaType 方法覆盖默认行为
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.Float("amount").
SchemaType(map[string]string{
dialect.MySQL: "decimal(6,2)", // Override MySQL.
dialect.Postgres: "numeric", // Override Postgres.
}),
}
}
GO Type
字段的默认类型是基本的Go数据类型,例如,对于字符串字段,类型为string, 对于时间字段,类型为time.Time
GoType 方法提供了一个选项,可以使用自定义类型替换默认的ent类型。但自定义类型必须是可以转换为Go的基本类型的类型,或者实现了ValueScanner接口的类型
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.Float("amount").
GoType(Amount(0)),
field.String("name").
Optional().
// A ValueScanner type.
GoType(&sql.NullString{}),
}
}
Default Values 默认值
Non-unique 的字段可以通过Default 和 UpdateDefault方法设置默认值
// Fields of the Group.
func (Group) Fields() []ent.Field {
return []ent.Field{
field.Time("created_at").
Default(time.Now),
field.Time("updated_at").
Default(time.Now).
UpdateDefault(time.Now),
}
}
Validators
关于字段的validator是通过 func(T) error 函数,该函数使用Validate方法在schema中定义,并在创建或更新schema的时候应用于字段的校验
字段的validator支持的类型有string 和所有的数字类型
// Fields of the Group.
func (Group) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Match(regexp.MustCompile("[a-zA-Z_]+$")).
Validate(func(s string) error {
if strings.ToLower(s) == s {
return errors.New("group name must begin with uppercase")
}
return nil
}),
}
}
内置的 Validators
ent orm 提供了一些内置的validators, 如下:
Numeric types:
Positive() - Positive adds a minimum value validator with the value of 1
Negative() - Negative adds a maximum value validator with the value of -1
NonNegative() - NonNegative adds a minimum value validator with the value of 0
Min(i) - Validate that the given value is > i.
Max(i) - Validate that the given value is < i.
Range(i, j) - Validate that the given value is within the range [i, j].string:
MinLen(i)
MaxLen(i)
Match(regexp.Regexp)
Optional 可选字段
可选字段是在创建的时候不是必传的字段,并将在数据库设置为可为空的字段
默认情况下,字段都是必填字段
Nillable
有时候你可能希望区分字段的零值和nil,如数据库的某列包含0 或者NULL,Nillable选项正是为此而存在的.
如果有一个类型为T的字段设置为Nillable ,在通过go generate 生成代码的时候的时候生成的struct 中改字段的类型是*T, 如果数据库中该字段是NULL, 那么在ent orm的查询结果中就是nil, 否则对于没有设置Nillable的字段,如果数据库中字段的值是NULl,返回的则是改字段的零值
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("required_name"),
field.String("optional_name").Optional(),
field.String("nilable_name").Optional().Nillable(),
field.String("nilable_name2").Optional().Nillable(),
field.Int("age").Optional(),
field.Int("age2").Optional().Nillable(),
}
}
我们通过如下代码进行数据的创建和查询,这里分别创建了两条数据,第一条数据的设置SetOptionalName,SetNilableName 的字段都没有设置内容,第二次的时候都设置了内容
package main
import (
"context"
"log"
"github.com/peanut-cc/ent_orm_notes/schema_notes/ent/user"
_ "github.com/go-sql-driver/mysql"
"github.com/peanut-cc/ent_orm_notes/schema_notes/ent"
)
func main() {
client, err := ent.Open("mysql", "root:123456@tcp(10.211.55.3:3306)/schema_notes?parseTime=True")
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
// run the auto migration tool
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("failed creating schema resources:%v", err)
}
client.User.Create().SetRequiredName("peanut").Save(ctx)
client.User.Create().SetRequiredName("syncd").
SetOptionalName("option_name").
SetNilableName("nil_name").
SetNilableName2("nil_name2").
SetAge(18).
SetAge2(20).
SaveX(ctx)
u := client.User.Query().Where(user.RequiredNameEQ("peanut")).OnlyX(ctx)
log.Printf("required_name is:%v option_name is:%v nil_name is:%v nil_name2 is:%v age is :%v age2 is:%v\n", u.RequiredName,
u.OptionalName, u.NilableName, u.NilableName2, u.Age, u.Age2)
u2 := client.User.Query().Where(user.RequiredNameEQ("syncd")).OnlyX(ctx)
log.Printf("required_name is:%v option_name is:%v nil_name is:%v nil_name2 is:%v age is :%v age2 is:%v\n", u2.RequiredName,
u2.OptionalName, u2.NilableName, u2.NilableName2, u2.Age, u2.Age2)
}
下面是数据的打印结果:
2020/08/26 20:39:47 required_name is:peanut option_name is: nil_name is:<nil> nil_name2 is:<nil> age is :0 age2 is:<nil>
2020/08/26 20:39:47 required_name is:syncd option_name is:option_name nil_name is:0xc000200580 nil_name2 is:0xc000200590 age is :18 age2 is:0xc00020c4d8
Immutable 不可变字段
不可变字段,是只能在创建的时候设置值
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Time("created_at").
Default(time.Now).
Immutable(),
}
}
Uniqueness 唯一索引
可以使用Unique方法给字段设置唯一索引。 注意:唯一所以字段不能具有默认值
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.String("nickname").
Unique(),
}
}
Storage Key
可以使用StorageKey方法配置自定义存储名称。在SQL中映射为列名
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
StorageKey(`old_name"`),
}
}
Indexes 索引
可以在多个字段和一些关系表中创建索引
Struct Tags
可以使用StructTag方法将自定义struct tag添加到生成的实体中。
请注意,如果未提供此选项,或者提供的该选项不包含json标记,则默认json标记将与字段名称一起添加。
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
StructTag(`gqlgen:"gql_name"`),
}
}
Sensitive Fields
可以使用Sensitive方法将字符串字段定义为Sensitive Fields。
Sensitive Fields不会被打印,并且在编码时将被忽略。
请注意,Sensitive Fields不能具有struct标记。
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("password").
Sensitive(),
}
}
Annotations
在代码生成中,Annotations用于将任意元数据附加到字段对象。模板扩展可以检索这个元数据并在它们的模板中使用它。注意,元数据对象必须可序列化为 JSON 原始值(例如,struct、 map 或 slice)。
// Fields of the user.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Time("creation_date").
Annotations(entgql.Annotation{
OrderField: "CREATED_AT",
}),
}
}
Edges
快速使用
Edges 也理解为表之间的association,通常指的我们表之间的一对多,多对多关系等。
ent/schema/pet.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// Pet holds the schema definition for the Pet entity.
type Pet struct {
ent.Schema
}
// Fields of the Pet.
func (Pet) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the Pet.
func (Pet) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("pets").
Unique(),
}
}
ent/schema/user.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Int("age"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("pets", Pet.Type),
edge.From("groups", Group.Type).Ref("users"),
}
}
ent/schema/group.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// Group holds the schema definition for the Group entity.
type Group struct {
ent.Schema
}
// Fields of the Group.
func (Group) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the Group.
func (Group) Edges() []ent.Edge {
return []ent.Edge{
edge.To("users", User.Type),
}
}
在上面的关系中,一个用户可以有多个宠物,但是一个宠物只能属于一个用户。所以这里对于宠物来说是一对一的关系,对于用户来说是多对一关系。
我们查看一下创建的pets表的信息:
CREATE TABLE `pets` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_bin NOT NULL,
`user_pets` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `pets_users_pets` (`user_pets`),
CONSTRAINT `pets_users_pets` FOREIGN KEY (`user_pets`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
因为在上述关系中:用户和宠物之间是一对多关系,所以这里使用的edge.To
;
而对宠物来说是一对一的关系,所以这里使用edge.From
的Ref
edge.To
和edge.From
是创建表关系的两个方法
一对一关系
在这个例子中,设定一个用户只能有一张信用卡,而一个信用卡也只能属于一个用户。
ent/schema/user.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Int("age"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("card", Card.Type).Unique(),
}
}
ent/schema/card.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// Card holds the schema definition for the Card entity.
type Card struct {
ent.Schema
}
// Fields of the Card.
func (Card) Fields() []ent.Field {
return []ent.Field{
field.String("number"),
field.Time("expired"),
}
}
// Edges of the Card.
func (Card) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("card").
Unique().
// We add the "Required" method to the builder
// to make this edge required on entity creation.
// i.e. Card cannot be created without its owner.
Required(),
}
}
一对多关系
在这个例子中用户和宠物之间是一对多关系,每个用户可以有多个宠物,一个宠物只有一个主人
ent/schema/user.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("pets", Pet.Type),
}
}
ent/schema/pet.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// Pet holds the schema definition for the Pet entity.
type Pet struct {
ent.Schema
}
// Fields of the Pet.
func (Pet) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the Pet.
func (Pet) Edges() []ent.Edge {
return []ent.Edge{
edge.From("owner", User.Type).
Ref("pets").
Unique(),
}
}
相关的查询如下代码:
package main
import (
"context"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
"github.com/peanut-cc/ent_orm_notes/one_to_many/ent"
)
func main() {
client, err := ent.Open("mysql", "root:123456@tcp(192.168.1.100:3306)/one_to_one?parseTime=True")
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
// run the auto migration tool
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("failed creating schema resources:%v", err)
}
Do(ctx, client)
}
func Do(ctx context.Context, client *ent.Client) error {
// Create the 2 pets.
pedro, err := client.Pet.
Create().
SetName("pedro").
Save(ctx)
if err != nil {
return fmt.Errorf("creating pet: %v", err)
}
lola, err := client.Pet.
Create().
SetName("lola").
Save(ctx)
if err != nil {
return fmt.Errorf("creating pet: %v", err)
}
// Create the user, and add its pets on the creation.
// 创建用户,并添加用户和宠物的关系
a8m, err := client.User.
Create().
SetName("a8m").
AddPets(pedro, lola).
Save(ctx)
if err != nil {
return fmt.Errorf("creating user: %v", err)
}
fmt.Println("User created:", a8m)shell
// Output: User(id=1, age=30, name=a8m)
// Query the owner. Unlike `Only`, `OnlyX` panics if an error occurs.
// 根据宠物反向查询所属的用户
owner := pedro.QueryOwner().OnlyX(ctx)
fmt.Println(owner.Name)
// Output: a8m
// Traverse the sub-graph. Unlike `Count`, `CountX` panics if an error occurs.
// 根据宠物反向查询用户,并查询该用户有多少宠物
count := pedro.
QueryOwner(). // a8m
QueryPets(). // pedro, lola
CountX(ctx) // count
fmt.Println(count)
// Output: 2
return nil
}
多对多关系
在这个例子中,用户和组之间是多对多关系,每个组有多个用户,每个用户也可以加入多个组
ent/schema/group.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// Group holds the schema definition for the Group entity.
type Group struct {
ent.Schema
}
// Fields of the Group.
func (Group) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the Group.
func (Group) Edges() []ent.Edge {
return []ent.Edge{
edge.To("users", User.Type),
}
}
ent/schema/user.go
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.From("groups", Group.Type).Ref("users"),
}
}
这个时候,会生成第三张表,group_users表,查看表的信息如下:
CREATE TABLE `group_users` (
`group_id` bigint(20) NOT NULL,
`user_id` bigint(20) NOT NULL,
PRIMARY KEY (`group_id`,`user_id`),
KEY `group_users_user_id` (`user_id`),
CONSTRAINT `group_users_group_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE,
CONSTRAINT `group_users_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
常用的查询方法:
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.From("groups", Group.Type).Ref("users"),
}
}
多对多(单张表)
这种关系其实也挺常见的,如我们微博账户,不同账户之间可以相关关注
package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.package schema
import (
"github.com/facebook/ent"
"github.com/facebook/ent/schema/edge"
"github.com/facebook/ent/schema/field"
)
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("following", User.Type).
From("followers"),
}
}
Field {
return []ent.Field{
field.String("name"),
}
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("following", User.Type).
From("followers"),
}
}
这样会生成一个user_following表,表信息为:
CREATE TABLE `user_following` (
`user_id` bigint(20) NOT NULL,
`follower_id` bigint(20) NOT NULL,
PRIMARY KEY (`user_id`,`follower_id`),
KEY `user_following_follower_id` (`follower_id`),
CONSTRAINT `user_following_follower_id` FOREIGN KEY (`follower_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `user_following_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
其他
在这里和在Fields中同样也有Required
StorageKey
Indexes
Annotations
用法基本一样,这里不再说明
延伸阅读
ent orm笔记2---schema使用(上)的更多相关文章
- ent orm笔记1---快速尝鲜
前几天看到消息Facebook孵化的ORM ent转为正式项目,出去好奇,简单体验了一下,使用上自己感觉比GORM好用,于是打算把官方的文档进行整理,也算是学习一下如何使用. 安装 ent orm 需 ...
- ent orm笔记4---Code Generation
在前面几篇文章中,我们经常使用的可能就是entc这个命令了,entc这个工具给带来了很多功能,这篇文章主要整理关于ent orm 中Code Generation 之前的例子中有个知识点少整理了,就是 ...
- ent orm笔记2---schema使用(下)
Indexes 索引 在前两篇的文章中,其实对于索引也有一些使用, 这里来详细看一下关于索引的使用 Indexes方法可以在一个或者多个字段上设置索引,以提高数据检索的速度或者定义数据的唯一性 在下面 ...
- ent 基本使用 一 schema 迁移
ent 是 facebook 开源的golang orm 框架,简单强大,以下是一个简单使用 环境准备 安装ent 工具 go get github.com/facebookincubator/ent ...
- c++学习笔记之封装篇(上)
title: c++学习笔记之封装篇(上) date: 2017-03-12 18:59:01 tags: [c++,c,封装,类] categories: [学习,程序员,c/c++] --- 一. ...
- Angular复习笔记7-路由(上)
Angular复习笔记7-路由(上) 关于Angular路由的部分将分为上下两篇来介绍.这是第一篇. 概述 路由所要解决的核心问题是通过建立URL和页面的对应关系,使得不同的页面可以用不同的URL来表 ...
- Spark学习笔记2——RDD(上)
目录 Spark学习笔记2--RDD(上) RDD是什么? 例子 创建 RDD 并行化方式 读取外部数据集方式 RDD 操作 转化操作 行动操作 惰性求值 Spark学习笔记2--RDD(上) 笔记摘 ...
- 简述C#中IO的应用 RabbitMQ安装笔记 一次线上问题引发的对于C#中相等判断的思考 ef和mysql使用(一) ASP.NET/MVC/Core的HTTP请求流程
简述C#中IO的应用 在.NET Framework 中. System.IO 命名空间主要包含基于文件(和基于内存)的输入输出(I/O)服务的相关基础类库.和其他命名空间一样. System.I ...
- Java系列笔记(6) - 并发(上)
目录 1,基本概念 2,volatile 3,atom 4,ThreadLocal 5,CountDownLatch和CyclicBarrier 6,信号量 7,Condition 8,Exchang ...
随机推荐
- PHP 5 echo 和 print 语句
PHP 5 echo 和 print 语句 在 PHP 中有两个基本的输出方式: echo 和 print. 本章节中我们会详细讨论两个语句的用法,并在实例中演示如何使用 echo 和 print. ...
- Python time altzone()方法
描述 Python time altzone() 函数返回格林威治西部的夏令时地区的偏移秒数.高佣联盟 www.cgewang.com 如果该地区在格林威治东部会返回负值(如西欧,包括英国).对夏令时 ...
- 探究:编程语言那么多,为什么偏偏是 C 语言成了大学的必修课?
谁叫你不幸生在中国了? ——何祚庥(中国科学院院士) 这是一本给非计算机专业的大学生的C语言的书.“我不是学计算机的,为啥要学C语言?”这个问题每年在中华大地都会被问上几百万次.被问的对象可能是老师, ...
- luogu P3829 [SHOI2012]信用卡凸包 凸包 点的旋转
LINK:信用卡凸包 当 R==0的时候显然是一个点的旋转 之后再求凸包即可. 这里先说点如何旋转 如果是根据原点旋转的话 经过一个繁杂的推导可以得到一个矩阵. [cosw,-sinw] [sinw, ...
- Jmeter TCP协议性能测试
最近有在做tcp协议性能测试,总结一下遇到的坑吧. 首先呢,我这边用的是16进制的报文: (1)TCPClient classname:org.apache.jmeter.protocol.tcp.s ...
- 通过MyBatis操作数据库
MyBatis是一款优秀的持久层框架,同样也是做OR Mapping的.与JPA不同,MyBatis里面需要我们自己来定制sql. MyBatis和JPA的选择 其实如果业务比较操作比较简单使用JPA ...
- 我靠!Semaphore里面居然有这么一个大坑!
这是why的第 59 篇原创文章 荒腔走板 大家好,我是why哥 ,欢迎来到我连续周更优质原创文章的第 59 篇. 上周写了一篇文章,一不小心戳到了大家的爽点,其中一个转载我文章的大号,阅读量居然突破 ...
- IDEA必备插件系列-Rainbow Brackets(彩虹括号)
Rainbow Brackets ,就是彩虹括号,各种鲜明颜色的括号 这个一个开源的项目: https://github.com/izhangzhihao/intellij-rainbow-brack ...
- C、C++、boost、Qt在嵌入式系统开发中的使用
概述 嵌入式系统开发相对来说属于偏底层的开发,也就是与硬件结合比较紧密,只能使用C/C++语言.对于做平台开发的人来说,C语言真的是很"古老"的语言,属于操作系统语言!好多人会觉得 ...
- 解决Xshell 工具连接不上VirtualBox虚拟机
初次尝试用VirtualBox安装Linux虚拟机,却遇到了一些问题,特地记录于此,方便后面查阅! 首先简易记录下安装Linux虚拟机过程: 大致经过如下步骤:新建虚拟电脑,加载Linux版本镜像安装 ...