精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Golang 數據庫事務實踐

數據庫
事務是很多業務的基礎,本文介紹了如何在Golang里實現數據庫事務操作,并以一個用戶注冊場景給出了完整實現。

Go 是一種年輕而強大的語言,專為編寫小型、簡單的服務而創建。但隨著時間推移,越來越多復雜應用和系統也在采用 Go 進行開發,這就出現了一些問題:如何處理事務?

為了深入探討這個問題,我們假設一個簡單的業務場景:用戶注冊。

作為一個系統,我希望在注冊時創建用戶和個人資料。

RDBMS/DBMS 的現代 Go 庫不像 C# 和 Java 的 Hibernate、Entity Framework 那樣強大,因此我們必須自己處理。為了實現用戶注冊業務場景,我們將創建并評估幾種處理事務的方法。

由于每種事務處理方法都必須與 sql.DB 和 sql.Tx 配合使用,因此需要引入接口來封裝對數據庫的訪問。

生成的應用有兩個域實體和一個用于訪問數據庫的 DB 低級接口。

package model

type User struct {
 Email string
}

type Profile struct {
 Name string
}
package transaction

type DB interface {
 QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
 QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
 ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
 PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}

準備工作完成后,就可以采用如下兩種方法。

1. 事務感知上下文

工作原理:transaction.Manager啟動事務并將其放入上下文。當存儲庫執行查詢時,助手會檢查上下文中是否有事務,并使用創建的事務來執行查詢,或者如果上下文為空,則不使用事務來執行查詢。

為了啟動事務,我們需要實體:Manager

package transaction

type Manager interface {
 Run(
  ctx context.Context,
  callback func(ctx context.Context) error,
 ) error
}

transaction.Manager 實現:

package transaction

import (
 "context"
 "database/sql"
 "github.com/pkg/errors"
 "go.uber.org/multierr"
)

type txKey string

var ctxWithTx = txKey("tx")

type SQLTransactionManager struct {
 db *sql.DB
}

func NewManager(db *sql.DB) *SQLTransactionManager {
 return &SQLTransactionManager{db: db}
}

func (m *SQLTransactionManager) Run(
 ctx context.Context,
 callback func(ctx context.Context) error,
) (rErr error) {
 tx, err := m.db.BeginTx(ctx, &sql.TxOptions{})
 if err != nil {
  return errors.WithStack(err)
 }

 defer func() {
  if rErr != nil {
   rErr = multierr.Combine(rErr, errors.WithStack(tx.Rollback()))
  }
 }()

 defer func() {
  if rec := recover(); rec != nil {
   if e, ok := rec.(error); ok {
    rErr = e
   } else {
    rErr = errors.Errorf("%s", rec)
   }
  }
 }()

 if err = callback(putTxToContext(ctx, tx)); err != nil {
  return err
 }

 return errors.WithStack(tx.Commit())
}

func ExtractTxFromContext(ctx context.Context) (*sql.Tx, bool) {
 tx := ctx.Value(ctxWithTx)

 if t, ok := tx.(*sql.Tx); ok {
  return t, true
 }

 return nil, false
}

func putTxToContext(ctx context.Context, tx *sql.Tx) context.Context {
 return context.WithValue(ctx, ctxWithTx, tx)
}

DB實現:

package storage

import (
 "brand/transaction/example1/transaction"
 "context"
 "database/sql"
)

type DB struct {
 db *sql.DB
}

func NewDB(db *sql.DB) *DB {
 return &DB{db: db}
}

func (d *DB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
 tx, ok := transaction.ExtractTxFromContext(ctx)
 if !ok {
  return d.db.QueryRowContext(ctx, query, args...)
 }

 return tx.QueryRowContext(ctx, query, args...)
}

func (d *DB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
 tx, ok := transaction.ExtractTxFromContext(ctx)
 if !ok {
  return d.db.QueryContext(ctx, query, args...)
 }

 return tx.QueryContext(ctx, query, args...)
}

func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) {
 tx, ok := transaction.ExtractTxFromContext(ctx)
 if !ok {
  return d.db.ExecContext(ctx, query, args...)
 }

 return tx.ExecContext(ctx, query, args...)
}

func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
 tx, ok := transaction.ExtractTxFromContext(ctx)
 if !ok {
  return d.db.PrepareContext(ctx, query)
 }

 return tx.PrepareContext(ctx, query)
}

RegistrationService 負責用戶注冊業務場景

package service

import (
 "brand/transaction/example1/model"
 "brand/transaction/example1/transaction"
 "context"
)

type UserRepository interface {
 Create(ctx context.Context, user *model.User) error
}

type ProfileRepository interface {
 Create(ctx context.Context, user *model.Profile) error
}

type RegistrationData struct {
 Email string
 Name  string
}

type RegistrationService struct {
 transactionManager transaction.Manager
 userRepository     UserRepository
 profileRepository  ProfileRepository
}

func NewRegistrationService(
 transactionManager transaction.Manager,
 userRepository UserRepository,
 profileRepository ProfileRepository,
) *RegistrationService {
 return &RegistrationService{
  transactionManager: transactionManager,
  userRepository:     userRepository,
  profileRepository:  profileRepository,
 }
}

func (s *RegistrationService) Register(ctx context.Context, data RegistrationData) error {
 return s.transactionManager.Run(ctx, func(ctx context.Context) error {
  if err := s.userRepository.Create(ctx, &model.User{
   Email: data.Email,
  }); err != nil {
   return err
  }

  if err := s.profileRepository.Create(ctx, &model.Profile{
   Name: data.Name,
  }); err != nil {
   return err
  }

  return nil
 })
}

User和ProfileRepository的實現:

package storage

import (
 "brand/transaction"
 "brand/transaction/example1/model"
 "context"
)

type ProfileRepository struct {
 db transaction.DB
}

func NewProfileRepository(db transaction.DB) *ProfileRepository {
 return &ProfileRepository{db: db}
}

func (r *ProfileRepository) Create(ctx context.Context, profile *model.Profile) error {
 _, err := r.db.ExecContext(ctx, "INSERT ...", profile.Name)

 return err
}
package storage

import (
 "brand/transaction"
 "brand/transaction/example1/model"
 "context"
)

type UserRepository struct {
 db transaction.DB
}

func NewUserRepository(db transaction.DB) *UserRepository {
 return &UserRepository{db: db}
}

func (r *UserRepository) Create(ctx context.Context, user *model.User) error {
 _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email)

 return err
}

優點:

  • 簡單:存儲庫會自動使用由 TransactionManager 啟動的事務
  • 與存儲無關:客戶端代碼對存儲類型一無所知

缺點

  • 不符合Go的使用習慣
  • 控制較少:無法防止在事務中啟動事務,可能會產生意想不到的副作用,代碼審查時必須考慮到這一點

2. 事務感知存儲庫

工作原理:事務管理器啟動事務并將事務放入回調,存儲庫工廠方法使用事務創建自己。

為了啟動事務,我們需要實體:Manager

type Manager interface {
 Run(
  ctx context.Context,
  callback func(ctx context.Context, tx *sql.Tx) error,
 ) error
}

transaction.Manager 實現:

package transaction

import (
 "context"
 "database/sql"
 "github.com/pkg/errors"
 "go.uber.org/multierr"
)

type txKey string

var ctxWithTx = txKey("tx")

type SQLTransactionManager struct {
 db *sql.DB
}

func NewManager(db *sql.DB) *SQLTransactionManager {
 return &SQLTransactionManager{db: db}
}

func (m *SQLTransactionManager) Run(
 ctx context.Context,
 callback func(ctx context.Context, tx *sql.Tx) error,
) (rErr error) {
 tx, err := m.db.BeginTx(ctx, &sql.TxOptions{})
 if err != nil {
  return errors.WithStack(err)
 }

 defer func() {
  if rErr != nil {
   rErr = multierr.Combine(rErr, errors.WithStack(tx.Rollback()))
  }
 }()

 defer func() {
  if rec := recover(); rec != nil {
   if e, ok := rec.(error); ok {
    rErr = e
   } else {
    rErr = errors.Errorf("%s", rec)
   }
  }
 }()

 if err = callback(ctx, tx); err != nil {
  return err
 }

 return errors.WithStack(tx.Commit())
}

DB實現:

package storage

import (
 "context"
 "database/sql"
)

type DB struct {
 db *sql.DB
}

func NewDB(db *sql.DB) *DB {
 return &DB{db: db}
}

func (d *DB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
 return d.db.QueryRowContext(ctx, query, args...)
}

func (d *DB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
 return d.db.QueryContext(ctx, query, args...)
}

func (d *DB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) {
 return d.db.ExecContext(ctx, query, args...)
}

func (d *DB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
 return d.db.PrepareContext(ctx, query)
}

RegistrationService 負責用戶注冊業務場景

有兩種方法可以創建帶有事務的存儲庫:

  • 存儲庫帶有結構方法 WithTransaction(示例中使用了該方法)
  • 存儲庫工廠 userRepositoryFactory.CreateFromTransaction(tx)
package service

import (
 "brand/transaction/example2/model"
 "brand/transaction/example2/transaction"
 "context"
 "database/sql"
)

type UserRepository interface {
 Create(ctx context.Context, user *model.User) error
 WithTransaction(tx *sql.Tx) UserRepository
}

type ProfileRepository interface {
 Create(ctx context.Context, user *model.Profile) error
 WithTransaction(tx *sql.Tx) ProfileRepository
}

type RegistrationData struct {
 Email string
 Name  string
}

type RegistrationService struct {
 transactionManager transaction.Manager
 userRepository     UserRepository
 profileRepository  ProfileRepository
}

func NewRegistrationService(
 transactionManager transaction.Manager,
 userRepository UserRepository,
 profileRepository ProfileRepository,
) *RegistrationService {
 return &RegistrationService{
  transactionManager: transactionManager,
  userRepository:     userRepository,
  profileRepository:  profileRepository,
 }
}

func (s *RegistrationService) Register(ctx context.Context, data RegistrationData) error {
 return s.transactionManager.Run(ctx, func(ctx context.Context, tx *sql.Tx) error {
  userRepository := s.userRepository.WithTransaction(tx)
  profileRepository := s.profileRepository.WithTransaction(tx)

  if err := userRepository.Create(ctx, &model.User{
   Email: data.Email,
  }); err != nil {
   return err
  }

  if err := profileRepository.Create(ctx, &model.Profile{
   Name: data.Name,
  }); err != nil {
   return err
  }

  return nil
 })
}

User和ProfileRepository的實現:

package storage

import (
 "brand/transaction"
 "brand/transaction/example2/model"
 "brand/transaction/example2/service"
 "context"
 "database/sql"
)

type ProfileRepository struct {
 db transaction.DB
}

func NewProfileRepository(db transaction.DB) *ProfileRepository {
 return &ProfileRepository{db: db}
}

func (r *ProfileRepository) Create(ctx context.Context, profile *model.Profile) error {
 _, err := r.db.ExecContext(ctx, "INSERT ...", profile.Name)

 return err
}

func (r *ProfileRepository) WithTransaction(tx *sql.Tx) service.ProfileRepository {
 return NewProfileRepository(tx)
}
package storage

import (
 "brand/transaction"
 "brand/transaction/example2/model"
 "brand/transaction/example2/service"
 "context"
 "database/sql"
)

type UserRepository struct {
 db transaction.DB
}

func NewUserRepository(db transaction.DB) *UserRepository {
 return &UserRepository{db: db}
}

func (r *UserRepository) Create(ctx context.Context, user *model.User) error {
 _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email)

 return err
}

func (r *UserRepository) WithTransaction(tx *sql.Tx) service.UserRepository {
 return NewUserRepository(tx)
}

優點:

  • 更明確:在注冊服務內部創建事務,可避免副作用

缺點:

  • 客戶端代碼知道存儲類型
  • 客戶端代碼負責創建新的存儲庫

我相信任何一種方法都能使代碼更易讀、更簡單,但建議使用第一種方法,從而可以隱藏存儲細節,使我們能夠在一個項目中使用多個存儲,而無需考慮實現和存儲細節。




package storage

import (
 "brand/transaction"
 "brand/transaction/example2/model"
 "brand/transaction/example2/service"
 "context"
 "database/sql"
)

type UserRepository struct {
 db transaction.DB
}

func NewUserRepository(db transaction.DB) *UserRepository {
 return &UserRepository{db: db}
}

func (r *UserRepository) Create(ctx context.Context, user *model.User) error {
 _, err := r.db.ExecContext(ctx, "INSERT ...", user.Email)

 return err
}

func (r *UserRepository) WithTransaction(tx *sql.Tx) service.UserRepository {
 return NewUserRepository(tx)
}
責任編輯:趙寧寧 來源: DeepNoMind
相關推薦

2020-11-18 10:16:52

數據庫回滾事務

2020-11-18 08:32:07

數據庫

2024-04-17 08:11:01

數據庫事務流程

2009-08-07 17:04:41

C#數據庫

2010-10-08 09:38:55

Android數據庫事

2009-09-24 14:12:22

Hibernate數據

2025-04-08 06:00:00

2021-04-09 08:21:25

數據庫索引數據

2020-11-23 14:16:42

Golang

2020-06-17 16:56:36

數據庫MySQL跨行事務

2017-08-22 17:10:45

數據庫MySQL事務模型

2017-06-22 16:00:07

數據庫NoSQL遷移實踐

2022-02-10 10:51:35

數據庫

2017-05-09 12:40:05

2013-10-08 09:54:41

數據庫安全數據庫管理

2023-06-30 13:22:19

2018-09-06 14:53:39

數據庫事務隔離隔離級別

2010-05-31 15:12:44

MySQL數據庫

2023-10-11 08:09:53

事務隔離級別
點贊
收藏

51CTO技術棧公眾號

欧美亚洲第一页| 欧美亚洲国产一卡| 91精品国产乱码久久蜜臀| 国产欧美 在线欧美| 做a视频在线观看| 99这里有精品视频| 美女视频免费精品| 国产日韩综合av| 久久久91精品国产一区不卡| 波多野结衣与黑人| 国产亚洲色婷婷久久99精品| 久久亚洲导航| 久久一本综合频道| 欧美精品一级二级三级| 国产精品中出一区二区三区| 四虎影成人精品a片| 欧洲不卡av| 懂色av一区二区三区免费观看| 亚洲偷欧美偷国内偷| 97碰在线视频| 国产孕妇孕交大片孕| 偷拍亚洲色图| 亚洲精品国产第一综合99久久| 国模精品视频一区二区三区| 在线观看免费污视频| 爽爽视频在线观看| 婷婷综合视频| 91成人看片片| 国产伦精品一区二区三毛| 日本老熟妇毛茸茸| 91伦理视频在线观看| 激情久久久久久| 欧美亚州韩日在线看免费版国语版| 亚洲欧洲另类精品久久综合| 全部毛片永久免费看| 日韩精品一级| 国产欧美日韩综合| 欧美第一淫aaasss性| 久久人人爽av| 日本视频在线观看| 成人av资源网站| 国产精品丝袜一区二区三区| 日韩久久久久久久久| 91欧美极品| 一区二区久久久久久| 免费国产一区二区| 日本三级免费看| 国产精品99一区二区三| 亚洲天堂男人天堂女人天堂| 波多野结衣办公室双飞| 久久久久久久性潮| 久久久99免费| 国产精品劲爆视频| 99热国产在线观看| 女人色偷偷aa久久天堂| 制服视频三区第一页精品| 亚洲精品视频一二三| 熟妇高潮一区二区高潮| 亚洲主播在线| 国模吧一区二区三区| 九九九免费视频| 日本一区二区三区播放| 欧美日韩精品欧美日韩精品一| 日韩亚洲欧美精品| 国产又大又粗又长| 久久99在线观看| 欧美xxxx18性欧美| 伊人影院综合在线| 深夜成人影院| 91成人免费电影| 蜜臀视频一区二区三区| 唐人社导航福利精品| 欧美视频不卡中文| 欧洲成人一区二区| 又骚又黄的视频| 欧美aaaa视频| www国产精品视频| 成年人的黄色片| 偷拍精品精品一区二区三区| 午夜精品久久久久影视| 国产中文一区二区| 精品成人av一区二区在线播放| 99久久影视| 久久人人爽人人爽爽久久| 精品国产国产综合精品| 久久神马影院| 欧美成人精品在线观看| 久久精品99久久久久久| 在线电影一区| 日本免费在线精品| 国产成人无码av| 四虎成人av| 久久手机精品视频| 2018国产精品| 极品束缚调教一区二区网站 | 国产在线欧美日韩| 天天影院图片亚洲| 日本一区二区久久| 日韩欧美一区二区三区四区| 五月香视频在线观看| 亚洲欧美国产77777| 波多野结衣av一区二区全免费观看| 不卡专区在线| 国产精品嫩草影院com| 99re在线视频观看| 欧美自拍第一页| 日韩av在线发布| 精品国产一区二区三区久久久| 成人无码av片在线观看| 99tv成人| 98精品国产自产在线观看| 久久精品视频7| 另类小说综合欧美亚洲| 国产乱码一区| av资源种子在线观看| 亚洲久草在线视频| 免费无码av片在线观看| 成人日韩欧美| 亚洲www啪成人一区二区麻豆| 欧美日韩在线免费播放| 精品亚洲a∨一区二区三区18| 亚洲第一男人av| 在线免费av播放| 2018av在线| 一级中文字幕一区二区| 国产成人精品无码播放| 日韩三级精品| 在线观看亚洲区| 国产午夜福利一区二区| 色综合久久一区二区三区| 亚洲精品xxxx| 欧洲一级黄色片| 久久精品免费一区二区三区| 69久久夜色精品国产69乱青草| 国产又黄又猛又爽| 国产欧美日韩一区二区三区在线观看| 蜜桃视频一区二区在线观看| 精品国产黄a∨片高清在线| 制服丝袜国产精品| 又黄又爽又色的视频| 久久香蕉国产| 国产成人精品电影久久久| 人妻久久一区二区| 残酷重口调教一区二区| 7777免费精品视频| 国产免费不卡视频| 精品无码三级在线观看视频| 成人av电影天堂| 久色视频在线| 疯狂做受xxxx高潮欧美日本| 亚洲天堂小视频| 先锋资源久久| 国产日韩在线播放| 91ph在线| 欧美日韩中文字幕精品| 激情五月亚洲色图| 日韩深夜影院| 777午夜精品福利在线观看| 97超碰国产在线| 中文字幕在线播放不卡一区| 欧美精品性生活| 精品国产aⅴ| 国产精品h片在线播放| 欧洲视频在线免费观看| 日韩欧美亚洲范冰冰与中字| 97免费公开视频| 欧美激情成人在线| 99久久精品无码一区二区毛片| 美女隐私在线观看| 欧美另类变人与禽xxxxx| 人妻互换一区二区激情偷拍| 日本免费新一区视频| 日韩欧美亚洲v片| 99九九久久| 综合网中文字幕| 日本妇女毛茸茸| 综合日韩在线| 1区1区3区4区产品乱码芒果精品| 久久99精品久久久久久野外| 3atv一区二区三区| 性色av无码久久一区二区三区| 国产综合一区二区| 在线观看成人免费| 国产精品18hdxxxⅹ在线| 亚洲精品一区二区三区精华液| 免费在线视频一区二区| 成人免费观看视频| 337p粉嫩大胆噜噜噜鲁| 欧美手机视频| 国产日韩欧美91| 最新电影电视剧在线观看免费观看| 欧美日韩另类国产亚洲欧美一级| 精品国产视频在线观看| 成人免费观看av| 国产精品无码一本二本三本色| 欧美oldwomenvideos| 51国偷自产一区二区三区的来源| 黄网在线免费看| 国产视频久久网| 中文字幕网址在线| 玉足女爽爽91| 日本黄色网址大全| 国内精品伊人久久久久av影院| 中文字幕精品—区二区日日骚| 亚洲视频精选| 日韩美女写真福利在线观看| 欧美a免费在线| 亚洲国产成人精品久久久国产成人一区 | 男人与禽猛交狂配| 99久久99久久久精品齐齐| 国产免费毛卡片| 99成人在线视频| 美女亚洲精品| 国产一区二区三区免费观看在线| 日韩av网址在线观看| 亚洲熟女www一区二区三区| 成人av手机在线观看| 国产精品拍拍拍| 狠久久av成人天堂| 天天综合狠狠精品| 成人在线视频中文字幕| 国产精品福利观看| 久久大胆人体| 日韩亚洲综合在线| 天天综合天天色| 欧美日韩不卡一区| 丰满人妻老熟妇伦人精品| 亚洲精品国产一区二区精华液 | 日韩有码电影| 日韩欧美的一区| 正在播放亚洲精品| 欧美日韩在线视频首页| 欧产日产国产v| 2023国产精品| 91女神在线观看| 一区二区三区高清视频在线观看| 国产日韩欧美夫妻视频在线观看| 97人澡人人添人人爽欧美| 久久久999国产| 粉嫩av一区| 亚洲国产精久久久久久久| 国产成人精品一区二区色戒| 亚洲第一搞黄网站| 一区二区视频免费看| 中文字幕精品综合| 亚洲第一成人网站| 91亚洲永久精品| 中文字幕在线视频播放| 国产91精品一区二区麻豆亚洲| 成人免费在线视频播放| 日本欧美视频| 日本不卡高清视频一区| 女同另类激情重口| 国产91精品入口17c| 欧美亚洲二区| 国产精品视频网址| 91cn在线观看| 精品久久久999| 天堂аⅴ在线地址8| 在线电影av不卡网址| 国产精品四虎| 亚洲区一区二区| 国产在线黄色| 亚洲欧美精品一区二区| 日韩在线免费看| 亚洲人成啪啪网站| 国产在线中文字幕| 一个色综合导航| 成人免费在线观看| 色小说视频一区| 国产九色在线| 亚洲第一中文字幕| 丰满人妻熟女aⅴ一区| 日韩欧美色电影| 亚洲av无码一区二区乱子伦 | 91成品人影院| 91精品国产91久久久久久一区二区 | 久久精品www| 午夜精品久久久久久久99樱桃 | 婷婷色综合网| 玖玖精品在线视频| 亚洲91视频| 日本五级黄色片| 国产欧美一区二区色老头 | 国产女人高潮时对白| 日韩一区二区三区免费观看| 丰满人妻一区二区三区四区53| 欧美大黄免费观看| 四虎在线免费观看| 中文字幕av一区中文字幕天堂 | 黄色资源网久久资源365| 91网址在线观看精品| 成人视屏免费看| 亚洲欧洲久久久| 久久综合资源网| 丰满少妇被猛烈进入一区二区| 亚洲综合视频在线观看| 在线观看日韩中文字幕| 欧美无乱码久久久免费午夜一区| 国产露脸无套对白在线播放| 日韩欧美成人午夜| 三级毛片在线免费看| 色婷婷久久一区二区| xxxcom在线观看| 国产精品自在线| 欧美成年网站| 日本精品一区二区| 欧美激情五月| 亚洲不卡视频在线| www.欧美精品一二区| 性猛交娇小69hd| 一区二区三区av电影| 中文字幕永久在线| 精品国产髙清在线看国产毛片| 福利成人在线观看| 色综合天天综合网国产成人网| 综合日韩av| 97久久天天综合色天天综合色hd| 亚洲欧美成人vr| 国产精品av免费观看| 国产午夜久久| 99久久综合网| 国产精品福利电影一区二区三区四区| 免费在线一区二区三区| 欧美私人免费视频| 亚洲 精品 综合 精品 自拍| 久久久精品在线| 精品日韩视频| 国产91精品久久久| 亚洲精品伦理| 欧美日韩一区在线观看视频| 青青草91久久久久久久久| 国产美女主播在线| 精品写真视频在线观看| 四虎国产精品成人免费入口| 亚洲欧美日韩电影| 国产精品尤物视频| 亚洲精品理论电影| 人交獸av完整版在线观看| 国产精品视频白浆免费视频| 亚洲69av| 欧美一区二区中文字幕| 精品一区二区三区久久久| 调教驯服丰满美艳麻麻在线视频| 国产欧美日韩激情| 国产成人精品网| 亚洲国产欧美一区二区丝袜黑人 | 日产精品一区二区| 看一级黄色录像| 免费看亚洲片| 一级国产黄色片| 亚洲国产一二三| 亚洲精品视频专区| 在线成人一区二区| 成人黄色视屏网站| 亚洲成人第一| 免费在线看成人av| av在线网站观看| 精品久久久久久国产| 婷婷在线免费观看| 久久久久久久久久久久av| 亚洲天堂av资源在线观看| 免费久久久久久| 国产一区二区三区蝌蚪| 国产女人18水真多毛片18精品| 欧美精品一级二级| 激情视频在线观看| 成人国产精品久久久| 国产精品久久观看| 在线观看中文av| 悠悠色在线精品| 国产草草影院ccyycom| 日韩中文字幕亚洲| 国产一区二区三区亚洲综合| 老司机午夜免费福利视频| 国产成人免费视频网站| 日本裸体美女视频| 日韩美女视频在线| 俺来俺也去www色在线观看| 好看的日韩精品视频在线| 国产视频一区免费看| 亚洲综合第一区| 日韩一级完整毛片| 91av久久| 日韩av免费电影| 久久66热偷产精品| 久久久久久免费观看| 日韩视频免费观看高清在线视频| 欧美人与禽性xxxxx杂性| 狠狠综合久久av| 日韩av在线免费观看不卡| 欧美成人久久久免费播放| 欧美一区二区三区免费大片| 国内老司机av在线| 免费成人深夜夜行视频| 精品一区二区免费视频| 动漫精品一区一码二码三码四码| 日韩av网址在线观看| 欧美大片网站| 男女啪啪免费视频网站|