Java Spring VS Go gRPC
Tags: restful, grpc, spring, java
Published: 2022-03-01 06:13:01

---
title: Java Spring VS Go gRPC
tags: restful, gRPC, spring, java
author: Corey Lai
---
# Spring boot 

## Client Side
以下 Entity 會以自動產生的方式給予 PR 
EntityController 則是底層功能
**Entity Model**
```java
@Entity  
@Table(name = "CAR")  
public class Car extends EntityContoller {  
	@Id  
	@Column(name = "ID")  
	@SerializedName("ID")  
	private Integer id;  
	
	@Column(name = "MODEL")  
	@SerializedName("MODEL")  
	private String model;  
	
	@Column(name = "MAKE")  
	@SerializedName("MAKE")  
	private String make;  
	
	@Column(name = "PREVIEW")  
	@SerializedName("PREVIEW")  
	private String preview;  
	
	@Column(name = "DESCRIPTION")  
	@SerializedName("DESCRIPTION")  
	private String description;  
	
	@Column(name = "PRICE")  
	@SerializedName("PRICE")  
	private Integer price;  
	
	public Car() {  
		super();  
	}  
	
	public Car(Integer id, String model, String make, String description, String preview, Integer price) {  
		super();  
		this.id = id;  
		this.model = model;  
		this.make = make;  
		this.preview = preview;  
		this.description = description;  
		this.price = price;  
	}  
	
	public Integer getId() {  
		return id;  
	}  
	
	public void setId(Integer id) {  
		this.id = id;  
	}  
	
	public String getMake() {  
		return make;  
	}  
	
	public void setMake(String make) {  
		this.make = make;  
	}  
	
	public String getPreview() {  
		return preview;  
	}  
	
	public void setPreview(String preview) {  
		this.preview = preview;  
	}  
	
	public String getDescription() {  
		return description;  
	}  
	
	public void setDescription(String description) {  
		this.description = description;  
	}  
	
	public Integer getPrice() {  
		return price;  
	}  
	
	public void setPrice(Integer price) {  
		this.price = price;  
	}  
	
	public String getModel() {  
		return model;  
	}  
	
	public void setModel(String model) {  
		this.model = model;  
	}  
}
```
```java
@Entity  
@Table(name = "CAR_INFO")  
public class CarInfo extends EntityContoller {  
	
	@Id  
	@SerializedName("ID")  
	@Column(name = "ID")  
	private Integer id;  
	
	@Id  
	@SerializedName("CAR_OWNER")  
	@Column(name = "CAR_OWNER")  
	private String carOwner;  
	
	@Column(name = "CAR_TYPE")  
	@SerializedName("CAR_TYPE")  
	private String carType;  
	
	@Temporal(TemporalType.TIMESTAMP)  
	@Column(name = "DRIVE_DATE")  
	@SerializedName("DRIVE_DATE")  
	private Date driveDate;  
	
	@Column(name = "KM")  
	@SerializedName("KM")  
	private String km;  
	
	@Column(name = "TEST")  
	@SerializedName("TEST")  
	private String test;  
	
	@ManyToOne  
	@SerializedName("*CAR")  
	@JoinColumn(name = "ID", referencedColumnName = "ID", insertable = false, updatable = false)  
	private Car car = new Car();  
	
	public CarInfo() {  
		super();  
	}  
	
	public CarInfo(Integer id, String carOwner, String carType, Date driveDate, String km, String test, Car car) {  
		super();
		this.id = id;
		this.carOwner = carOwner;
		this.carType = carType;
		this.driveDate = driveDate;
		this.km = km;
		this.test = test;
		this.car = car;
	}  
	
	public Integer getId() {  
		return id;  
	}  
	public void setId(Integer value) {  
		id = value;  
	}  
	
	public String getCarOwner() {  
		return carOwner;  
	}  
	public void setCarOwner(String value) {  
		carOwner = value;  
	}  
	
	public Date getDriveDate() {  
		return driveDate;  
	}  
	public void setDriveDate(Date value) {  
		driveDate = value;  
	}  
	
	public String getKm() {  
		return km;  
	}  
	public void setKm(String value) {  
		km = value;  
	}  
	
	public String getTest() {  
		return test;  
	}  
	public void setTest(String value) {  
		test = value;  
	}  
	
	public String getCarType() {  
		return carType;  
	}  
	public void setCarType(String value) {  
		carType = value;  
	}  
	
	public Car getCar() {  
		return car;  
	}  
	public void setCar(Car value) {  
		car = value;  
	}  
}
```
ViewModel
```java 
public class CarInfoViewModel extends ServiceImpl {  

	private String keyword;  
	private List<CarInfo> list;  
	private CarInfo selectedCarInfo;  

	private CarService carService = new CarServiceImpl();  
	public CarInfoViewModel() {
		super(new CarInfo());  
	}

	public List<CarInfo> getList(){  
		return list;  
	}  

	public void setSelectedCarInfo(CarInfo selectedCarInfo) {  
		this.selectedCarInfo = selectedCarInfo;  
	}  

	public CarInfo getSelectedCarInfo() {  
		return selectedCarInfo;  
	}  

	@Command  
	@NotifyChange("list")  
	public void search(){  
		list = carService.search(keyword);  
	}  
	public List<CarInfo> get {
		RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), gson.toJson(new CarInfo()));  
		retrofit2.Call<ResponseBody> call = entityServices.getNativeCarInfo(body);
		Type type = new TypeToken<List<CarInfo>>() {}.getType();  

		call.enqueue(new Callback<ResponseBody>() {  

			@Override  
			public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {  
				try {  
					list = gson.fromJson(response.body().string(), type);  
				} catch (Exception e) {  
					e.printStackTrace();  
				}  
			}  

			@Override  
			public void onFailure(Call<ResponseBody> call, Throwable t) {  
				System.out.println("Connection Failed");  
			}  
		});
	}

}
```
View
```html
<window title="Search" width="600px" border="normal" apply="org.zkoss.bind.BindComposer"
		viewModel="@id('vm') @init('demo.getting_started.mvvm.SearchViewModel')">
	<hbox align="center">
		Keyword:
		<textbox value="@bind(vm.keyword)" />
		<button label="Search" iconSclass="z-icon-search" onClick="@command('search')" />
	</hbox>
	<listbox height="160px" model="@bind(vm.carInfoList)" emptyMessage="No car found in the result"
			 selectedItem="@bind(vm.selectedCarInfo)" style="margin-top:10px">
		<listhead>
			<listheader label="ID" />
			<listheader label="Car Owner" />
			<listheader label="Car Type" />
			<listheader label="Drive Date" />
			<listheader label="KM" />
		</listhead>
		<template name="model">
			<listitem>
				<listcell label="@bind(each.id)"></listcell>
				<listcell label="@bind(each.carOwner)"></listcell>
				<listcell label="@bind(each.carType)"></listcell>
				<listcell label="@bind(each.driveDate) @converter('formatedDate',format='yyyy/MM/dd')"></listcell>
				<listcell label="@bind(each.km)"></listcell>
			</listitem>
		</template>
	</listbox>
	<hbox style="margin-top:20px" visible="@bind(not empty vm.selectedCarInfo)">
		<image src="@bind(vm.selectedCarInfo.car.preview)" style="padding:10px" />
		<vbox>
			<hlayout>
				Model : <label value="@bind(vm.selectedCarInfo.car.model)" style="font-weight:bold"/>
			</hlayout>
			<hlayout>
				Make : <label value="@bind(vm.selectedCarInfo.car.make)" style="font-weight:bold"/>
			</hlayout>
			<hlayout>
				Price :
				<span>$<label value="@bind(vm.selectedCarInfo.car.price)" style="font-weight:bold"/></span>
			</hlayout>
			<label value="@bind(vm.selectedCarInfo.car.description)" />
		</vbox>
	</hbox>
</window>

```

## Server Side
PR 如需複雜方式必須去 Spring Boot 新增，並將 Entity Model 放入Spring Boot

**CarInfoContoller**
```java
@RestController  
@RequestMapping("/carinfo")  
public class CarInfoContoller {  
	
	@Autowired  
	private CarInfoRepository carInfoRepository;  
	
	@Autowired  
	CarInfoService CarInfoService;  
	
	@GetMapping("/paging") // native sql  
	public List<CarInfo> getCarInfoByNativeSQL(@PageableDefault(size = 10) Pageable pageable) {  
		Page<CarInfo> contacts = this.carInfoRepository.findAllUsersWithPaginationByNativeSQL(pageable);  
		return contacts.getContent();  
	}
}
```
```java
@Repository  
public interface CarInfoRepository extends JpaRepository<CarInfo, Long> {
	@Query(value = "SELECT * FROM CarInfo ORDERBY CAR_OWNER", countQuery = "SELECT count(*) FROM CarInfo", nativeQuery = true)  
	Page<CarInfo> findAllUsersWithPaginationByNativeSQL(Pageable pageable);
}

```

##JpaRepository 支援關鍵字方法語法

| Keyword | Sample | JPQL snippet |
|--|--|--|
| And | findByLastnameAndFirstname|… where x.lastname = ?1 and x.firstname = ?2 |
| Or| findByLastnameOrFirstname|… where x.lastname = ?1 or x.firstname = ?2 |
| Between| findByStartDateBetween|… where x.startDate between 1? and ?2 |
| LessThan| findByAgeLessThan|… where x.age < ?1|
| GreaterThan| findByAgeGreaterThan|… where x.age > ?1|
| After| findByStartDateAfter|… where x.startDate > ?1 |
| Before| findByStartDateBefore|… where x.startDate < ?1|
| IsNull| findByAgeIsNull|… where x.age is null|
| IsNotNull,NotNull| findByAge(Is)NotNull|… where x.age not null|
| Like| findByFirstnameLike|… where x.firstname like ?1 |
| NotLike| findByFirstnameNotLike|… where x.firstname not like ?1|
| StartingWith| findByFirstnameStartingWith|… where x.firstname like ?1 (parameter bound with appended %)|
| EndingWith| findByFirstnameEndingWith|… where x.firstname like ?1 (parameter bound with prepended %)|
| Containing| findByFirstnameContaining|… where x.firstname like ?1  (parameter bound wrapped in  %)|
| OrderBy| findByAgeOrderByLastnameDesc|… where x.age = ?1 order by x.lastname desc|
| Not| findByLastnameNot|… where x.lastname <> ?1|
| In| findByAgeIn(```Collection<Age> ages```)|… where x.age in ?1 |
| NotIn| findByAgeNotIn(```Collection<Age> age```)|… where x.age not in ?1 |
| True| findByActiveTrue()|… where x.active = true |
| False| findByActiveFalse()|… where x.active = false |

# gRPC 

```bash
#透過 protoc tool 產生code
$ protoc -I car_info/ car_info/car_info.proto --go_out=plugins=grpc:car_info
#透過 maven plugin  產生code 
$ mvn protobuf:compile
```
## Golang Service Side
```go
syntax = "proto3";
// 生成的程式在 Golang 中將會屬於 `pb` 套件。
option java_multiple_files = true;
option java_package = "io.grpc.examples.car_info";
option java_outer_classname = "CarInfoProto";
option objc_class_prefix = "CARINFO";

package  car_info;
// CarInfo 定義了一個服務。
service  CarInfoService {
// GetList 會接收 CarInfoGetListRequest 資料，最終會回傳 CarInfoGetListResponse
rpc  GetList (CarInfoGetListRequest) returns (CarInfoGetListResponse) {}
rpc  InsertList (CarInfoInsertListRequest) returns (CarInfoInsertListResponse) {}
}

message  CarInfo {
	string  CarOwner = 1;
	string  CarType = 2;
	string  DriveDate = 3;
	string  km = 4;
	string  test = 5;
}

// Car Info Get List 需求 list
message  CarInfoGetListRequest { }
// Car Info Get List 回應 list
message  CarInfoGetListResponse {
	repeated  CarInfo  cars = 1;
}

// Car Info Get List 需求list
message  CarInfoInsertListRequest {
	repeated  CarInfo  cars = 1;
}
// Car Info Get List 回應 list
message  CarInfoInsertListResponse {
	repeated  CarInfo  cars = 1;
}
```
透過工具產生 code go 版本
```go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: car_info.proto
package car_info

import (
	context "context"
	fmt "fmt"
	math "math"

	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

type CarInfo struct {
	CarOwner             string   `protobuf:"bytes,1,opt,name=CarOwner,proto3" json:"CarOwner,omitempty"`
	CarType              string   `protobuf:"bytes,2,opt,name=CarType,proto3" json:"CarType,omitempty"`
	DriveDate            string   `protobuf:"bytes,3,opt,name=DriveDate,proto3" json:"DriveDate,omitempty"`
	Km                   string   `protobuf:"bytes,4,opt,name=km,proto3" json:"km,omitempty"`
	Test                 string   `protobuf:"bytes,5,opt,name=test,proto3" json:"test,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *CarInfo) Reset()         { *m = CarInfo{} }
func (m *CarInfo) String() string { return proto.CompactTextString(m) }
func (*CarInfo) ProtoMessage()    {}
func (*CarInfo) Descriptor() ([]byte, []int) {
	return fileDescriptor_6109e1b82e86989a, []int{0}
}

func (m *CarInfo) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_CarInfo.Unmarshal(m, b)
}
func (m *CarInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_CarInfo.Marshal(b, m, deterministic)
}
func (m *CarInfo) XXX_Merge(src proto.Message) {
	xxx_messageInfo_CarInfo.Merge(m, src)
}
func (m *CarInfo) XXX_Size() int {
	return xxx_messageInfo_CarInfo.Size(m)
}
func (m *CarInfo) XXX_DiscardUnknown() {
	xxx_messageInfo_CarInfo.DiscardUnknown(m)
}

var xxx_messageInfo_CarInfo proto.InternalMessageInfo

func (m *CarInfo) GetCarOwner() string {
	if m != nil {
		return m.CarOwner
	}
	return ""
}

func (m *CarInfo) GetCarType() string {
	if m != nil {
		return m.CarType
	}
	return ""
}

func (m *CarInfo) GetDriveDate() string {
	if m != nil {
		return m.DriveDate
	}
	return ""
}

func (m *CarInfo) GetKm() string {
	if m != nil {
		return m.Km
	}
	return ""
}

func (m *CarInfo) GetTest() string {
	if m != nil {
		return m.Test
	}
	return ""
}

// Car Info Get List 需求 list
type CarInfoGetListRequest struct {
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *CarInfoGetListRequest) Reset()         { *m = CarInfoGetListRequest{} }
func (m *CarInfoGetListRequest) String() string { return proto.CompactTextString(m) }
func (*CarInfoGetListRequest) ProtoMessage()    {}
func (*CarInfoGetListRequest) Descriptor() ([]byte, []int) {
	return fileDescriptor_6109e1b82e86989a, []int{1}
}

func (m *CarInfoGetListRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_CarInfoGetListRequest.Unmarshal(m, b)
}
func (m *CarInfoGetListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_CarInfoGetListRequest.Marshal(b, m, deterministic)
}
func (m *CarInfoGetListRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_CarInfoGetListRequest.Merge(m, src)
}
func (m *CarInfoGetListRequest) XXX_Size() int {
	return xxx_messageInfo_CarInfoGetListRequest.Size(m)
}
func (m *CarInfoGetListRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_CarInfoGetListRequest.DiscardUnknown(m)
}

var xxx_messageInfo_CarInfoGetListRequest proto.InternalMessageInfo

// Car Info Get List 回應 list
type CarInfoGetListResponse struct {
	Cars                 []*CarInfo `protobuf:"bytes,1,rep,name=cars,proto3" json:"cars,omitempty"`
	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
	XXX_unrecognized     []byte     `json:"-"`
	XXX_sizecache        int32      `json:"-"`
}

func (m *CarInfoGetListResponse) Reset()         { *m = CarInfoGetListResponse{} }
func (m *CarInfoGetListResponse) String() string { return proto.CompactTextString(m) }
func (*CarInfoGetListResponse) ProtoMessage()    {}
func (*CarInfoGetListResponse) Descriptor() ([]byte, []int) {
	return fileDescriptor_6109e1b82e86989a, []int{2}
}

func (m *CarInfoGetListResponse) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_CarInfoGetListResponse.Unmarshal(m, b)
}
func (m *CarInfoGetListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_CarInfoGetListResponse.Marshal(b, m, deterministic)
}
func (m *CarInfoGetListResponse) XXX_Merge(src proto.Message) {
	xxx_messageInfo_CarInfoGetListResponse.Merge(m, src)
}
func (m *CarInfoGetListResponse) XXX_Size() int {
	return xxx_messageInfo_CarInfoGetListResponse.Size(m)
}
func (m *CarInfoGetListResponse) XXX_DiscardUnknown() {
	xxx_messageInfo_CarInfoGetListResponse.DiscardUnknown(m)
}

var xxx_messageInfo_CarInfoGetListResponse proto.InternalMessageInfo

func (m *CarInfoGetListResponse) GetCars() []*CarInfo {
	if m != nil {
		return m.Cars
	}
	return nil
}

type CarInfoInsertListRequest struct {
	Cars                 []*CarInfo `protobuf:"bytes,1,rep,name=cars,proto3" json:"cars,omitempty"`
	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
	XXX_unrecognized     []byte     `json:"-"`
	XXX_sizecache        int32      `json:"-"`
}

func (m *CarInfoInsertListRequest) Reset()         { *m = CarInfoInsertListRequest{} }
func (m *CarInfoInsertListRequest) String() string { return proto.CompactTextString(m) }
func (*CarInfoInsertListRequest) ProtoMessage()    {}
func (*CarInfoInsertListRequest) Descriptor() ([]byte, []int) {
	return fileDescriptor_6109e1b82e86989a, []int{3}
}

func (m *CarInfoInsertListRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_CarInfoInsertListRequest.Unmarshal(m, b)
}
func (m *CarInfoInsertListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_CarInfoInsertListRequest.Marshal(b, m, deterministic)
}
func (m *CarInfoInsertListRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_CarInfoInsertListRequest.Merge(m, src)
}
func (m *CarInfoInsertListRequest) XXX_Size() int {
	return xxx_messageInfo_CarInfoInsertListRequest.Size(m)
}
func (m *CarInfoInsertListRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_CarInfoInsertListRequest.DiscardUnknown(m)
}

var xxx_messageInfo_CarInfoInsertListRequest proto.InternalMessageInfo

func (m *CarInfoInsertListRequest) GetCars() []*CarInfo {
	if m != nil {
		return m.Cars
	}
	return nil
}

// Car Info Get List 回應 list
type CarInfoInsertListResponse struct {
	Cars                 []*CarInfo `protobuf:"bytes,1,rep,name=cars,proto3" json:"cars,omitempty"`
	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
	XXX_unrecognized     []byte     `json:"-"`
	XXX_sizecache        int32      `json:"-"`
}

func (m *CarInfoInsertListResponse) Reset()         { *m = CarInfoInsertListResponse{} }
func (m *CarInfoInsertListResponse) String() string { return proto.CompactTextString(m) }
func (*CarInfoInsertListResponse) ProtoMessage()    {}
func (*CarInfoInsertListResponse) Descriptor() ([]byte, []int) {
	return fileDescriptor_6109e1b82e86989a, []int{4}
}

func (m *CarInfoInsertListResponse) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_CarInfoInsertListResponse.Unmarshal(m, b)
}
func (m *CarInfoInsertListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_CarInfoInsertListResponse.Marshal(b, m, deterministic)
}
func (m *CarInfoInsertListResponse) XXX_Merge(src proto.Message) {
	xxx_messageInfo_CarInfoInsertListResponse.Merge(m, src)
}
func (m *CarInfoInsertListResponse) XXX_Size() int {
	return xxx_messageInfo_CarInfoInsertListResponse.Size(m)
}
func (m *CarInfoInsertListResponse) XXX_DiscardUnknown() {
	xxx_messageInfo_CarInfoInsertListResponse.DiscardUnknown(m)
}

var xxx_messageInfo_CarInfoInsertListResponse proto.InternalMessageInfo

func (m *CarInfoInsertListResponse) GetCars() []*CarInfo {
	if m != nil {
		return m.Cars
	}
	return nil
}

func init() {
	proto.RegisterType((*CarInfo)(nil), "car_info.CarInfo")
	proto.RegisterType((*CarInfoGetListRequest)(nil), "car_info.CarInfoGetListRequest")
	proto.RegisterType((*CarInfoGetListResponse)(nil), "car_info.CarInfoGetListResponse")
	proto.RegisterType((*CarInfoInsertListRequest)(nil), "car_info.CarInfoInsertListRequest")
	proto.RegisterType((*CarInfoInsertListResponse)(nil), "car_info.CarInfoInsertListResponse")
}

func init() { proto.RegisterFile("car_info.proto", fileDescriptor_6109e1b82e86989a) }

var fileDescriptor_6109e1b82e86989a = []byte{
	// 309 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0xdd, 0x4a, 0x02, 0x41,
	0x14, 0x6e, 0x56, 0x4b, 0x3d, 0x85, 0xd0, 0x81, 0x6a, 0x94, 0x20, 0xd9, 0x08, 0xbc, 0xda, 0x0b,
	0xa3, 0xeb, 0xf0, 0x87, 0x42, 0x08, 0x95, 0x2d, 0xe8, 0x32, 0xa6, 0xe5, 0x18, 0x8b, 0xb9, 0xb3,
	0xcd, 0x4c, 0x56, 0x97, 0xbd, 0x4a, 0x6f, 0xd1, 0xdb, 0x85, 0xd3, 0xb8, 0x56, 0x5b, 0x51, 0x77,
	0x73, 0xbe, 0x3f, 0xbe, 0x33, 0x1c, 0xa8, 0x46, 0x42, 0x5d, 0xc5, 0xc9, 0x58, 0x06, 0xa9, 0x92,
	0x46, 0x62, 0x79, 0x31, 0xfb, 0xcf, 0x0c, 0x4a, 0x5d, 0xa1, 0xfa, 0xc9, 0x58, 0x62, 0x1d, 0xca,
	0x5d, 0xa1, 0x86, 0x0f, 0x09, 0x29, 0xce, 0x1a, 0xac, 0x59, 0x09, 0xb3, 0x19, 0xb9, 0x95, 0x5d,
	0x3c, 0xa5, 0xc4, 0x3d, 0x4b, 0x2d, 0x46, 0xdc, 0x85, 0x4a, 0x4f, 0xc5, 0x33, 0xea, 0x09, 0x43,
	0xbc, 0x60, 0xb9, 0x25, 0x80, 0x55, 0xf0, 0x26, 0x53, 0x5e, 0xb4, 0xb0, 0x37, 0x99, 0x22, 0x42,
	0xd1, 0x90, 0x36, 0x7c, 0xd5, 0x22, 0xf6, 0xed, 0xef, 0xc0, 0x96, 0xab, 0x70, 0x4a, 0xe6, 0x2c,
	0xd6, 0x26, 0xa4, 0xbb, 0xfb, 0x39, 0x71, 0x0c, 0xdb, 0x5f, 0x09, 0x9d, 0xca, 0x44, 0x13, 0x1e,
	0x40, 0x31, 0x12, 0x4a, 0x73, 0xd6, 0x28, 0x34, 0xd7, 0x5b, 0x9b, 0x41, 0xb6, 0x9f, 0xd3, 0x87,
	0x96, 0xf6, 0xdb, 0xc0, 0x1d, 0xd0, 0x4f, 0x34, 0xa9, 0x8f, 0xe1, 0x7f, 0x8d, 0xe8, 0x40, 0xed,
	0x9b, 0x88, 0x7f, 0xd5, 0x68, 0xbd, 0x32, 0xa8, 0x3a, 0xe4, 0x9c, 0xd4, 0x2c, 0x8e, 0x08, 0x07,
	0x50, 0x72, 0x3b, 0xe1, 0x5e, 0xce, 0xf6, 0xf9, 0x1b, 0xea, 0x8d, 0x9f, 0x05, 0xef, 0x3d, 0xfc,
	0x15, 0xbc, 0x04, 0x58, 0xf6, 0x43, 0x3f, 0xe7, 0xc8, 0xed, 0x5f, 0xdf, 0xff, 0x55, 0xb3, 0x08,
	0xee, 0x1c, 0x41, 0x2d, 0x96, 0xc1, 0x8d, 0x4a, 0xa3, 0x80, 0x1e, 0xc5, 0x34, 0xbd, 0x25, 0x9d,
	0x19, 0x3b, 0x1b, 0xce, 0x39, 0x9a, 0x5f, 0xd5, 0x88, 0xbd, 0x78, 0xa5, 0x6e, 0x3b, 0xec, 0x0f,
	0x4e, 0x86, 0xd7, 0x6b, 0xf6, 0xd0, 0x0e, 0xdf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x22, 0xe2, 0x10,
	0xb0, 0x7a, 0x02, 0x00, 0x00,
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4

// CarInfoServiceClient is the client API for CarInfoService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CarInfoServiceClient interface {
	// GetList 會接收 CarInfoGetListRequest 資料，最終會回傳 CarInfoGetListResponse
	GetList(ctx context.Context, in *CarInfoGetListRequest, opts ...grpc.CallOption) (*CarInfoGetListResponse, error)
	// rpc GetList (CarInfoGetListRequest) returns (stream CarInfo) {}
	InsertList(ctx context.Context, in *CarInfoInsertListRequest, opts ...grpc.CallOption) (*CarInfoInsertListResponse, error)
}

type carInfoServiceClient struct {
	cc *grpc.ClientConn
}

func NewCarInfoServiceClient(cc *grpc.ClientConn) CarInfoServiceClient {
	return &carInfoServiceClient{cc}
}

func (c *carInfoServiceClient) GetList(ctx context.Context, in *CarInfoGetListRequest, opts ...grpc.CallOption) (*CarInfoGetListResponse, error) {
	out := new(CarInfoGetListResponse)
	err := c.cc.Invoke(ctx, "/car_info.CarInfoService/GetList", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *carInfoServiceClient) InsertList(ctx context.Context, in *CarInfoInsertListRequest, opts ...grpc.CallOption) (*CarInfoInsertListResponse, error) {
	out := new(CarInfoInsertListResponse)
	err := c.cc.Invoke(ctx, "/car_info.CarInfoService/InsertList", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// CarInfoServiceServer is the server API for CarInfoService service.
type CarInfoServiceServer interface {
	// GetList 會接收 CarInfoGetListRequest 資料，最終會回傳 CarInfoGetListResponse
	GetList(context.Context, *CarInfoGetListRequest) (*CarInfoGetListResponse, error)
	// rpc GetList (CarInfoGetListRequest) returns (stream CarInfo) {}
	InsertList(context.Context, *CarInfoInsertListRequest) (*CarInfoInsertListResponse, error)
}

func RegisterCarInfoServiceServer(s *grpc.Server, srv CarInfoServiceServer) {
	s.RegisterService(&_CarInfoService_serviceDesc, srv)
}

func _CarInfoService_GetList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(CarInfoGetListRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(CarInfoServiceServer).GetList(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/car_info.CarInfoService/GetList",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(CarInfoServiceServer).GetList(ctx, req.(*CarInfoGetListRequest))
	}
	return interceptor(ctx, in, info, handler)
}

func _CarInfoService_InsertList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(CarInfoInsertListRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(CarInfoServiceServer).InsertList(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/car_info.CarInfoService/InsertList",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(CarInfoServiceServer).InsertList(ctx, req.(*CarInfoInsertListRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _CarInfoService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "car_info.CarInfoService",
	HandlerType: (*CarInfoServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "GetList",
			Handler:    _CarInfoService_GetList_Handler,
		},
		{
			MethodName: "InsertList",
			Handler:    _CarInfoService_InsertList_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "car_info.proto",
}
```
Entity 結構
```go
type  CarInfo  struct {
	CarOwner string
	CarType string
	DriveDate time.Time
	Km string
	Test string
}
```
取得清單方法
```go
func (s *server) GetList(ctx context.Context, in *car_info.CarInfoGetListRequest) (*car_info.CarInfoGetListResponse, error) {
	var list []CarInfo
	car_list := []*car_info.CarInfo{}

	engine.Limit(1000, 0).Find(&list)
	orm, _ := json.Marshal(list)
	json.Unmarshal(orm, &car_list)

	return &car_info.CarInfoGetListResponse{Cars: car_list}, nil
}
```
批次新增
```go
func (s *server) InsertList(ctx context.Context, in *car_info.CarInfoInsertListRequest) (*car_info.CarInfoInsertListResponse, error) {
	var list []CarInfo

	orm, _ := json.Marshal(in.Cars)
	json.Unmarshal(orm, &list)
	for _, car := range list {
		engine.Insert(&car)
	}

	return &car_info.CarInfoInsertListResponse{Cars: in.Cars}, nil
}
```
## Java Client Side
```java
public class CarInfoClient {
    private static final Logger logger = Logger.getLogger(CarInfoClient.class.getName());

    private final ManagedChannel channel;
    private final ServiceGrpc.ServiceBlockingStub blockingStub;

    public CarInfoClient(String host, int port) {
        this(ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build());
    }

    CarInfoClient(ManagedChannel channel) {
        this.channel = channel;
        blockingStub = ServiceGrpc.newBlockingStub(channel);
    }

    public void getList() {
        CarInfoGetListRequest request = CarInfoGetListRequest.newBuilder().build();
        CarInfoGetListResponse response;
        try {
            response = blockingStub.getList(request);
        } catch (StatusRuntimeException e) {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("CarList: " + response.getCarsList());
    }


    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    public static void main(String[] args) throws Exception {
        CarInfoClient client = new CarInfoClient("localhost", 50051);
        try {
            client.getList();
        } finally {
            client.shutdown();
        }
    }
}
```

## Golang Client Side
```go
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("連線失敗：＿%v", err)
	}
	defer conn.Close()
	car_service := car_info.NewCarInfoServiceClient(conn)
		
	// insert
	var list []*car_info.CarInfo
	for i := 1; i <= 10; i++ {
		list = append(list, &car_info.CarInfo{CarOwner: strconv.Itoa(counter) + ":" + strconv.Itoa(i), CarType: "n"})
	}
	car_service.InsertList(context.Background(), &car_info.CarInfoInsertListRequest{Cars: list})
}
```