Các bạn xem qua ở phần trước đã có hướng dẫn cài đặt Node.js trước khi tới phần này.Hướng dẫn nhanh phần cài đặt Mongodb.
Hướng dẫn cài đặt Mongo db phiên bản mới nhất và tool Robomongo quản lý (nó giống các tool quản lý mysql như Naviacat )
Cách cài đặt riêng từng gói .Truy cập trang chủ, download bản cài đặt: https://www.mongodb.org/downloads#production
Lưu ý: Bạn nên sử dụng hệ điều hành 64 bit, không khuyến khích sử dụng hệ điều hành 32 bit
Gói cài đặt điển hình (như ảnh chụp màn hình) là mongodb-win32-x86_64-3.2.3-signed.msi có dung lượng 93 MB
Tiếp tục cài đặt và tới finish
Sau đó cài đặt biến môi trường như sau :
Bấm tổ hợp phím Windows + R để gọi tiện ích Run, gõ systempropertiesadvanced để vào chương trình thiết lập biến môi trường.
thêm text : ;C:\Program Files\MongoDB\Server\3.2\bin vào cuối bước 3
Mở CMD và gõ lệnh : mongod -version
Hiện lên màn hình sau nếu thành công
Sau đó tạo thư mục để chứa Data base như hình sau :
Nội dung file config.txt
##store data
dbpath=C:mongodb\data
##all output go here
logpath=C:\mongodb\log\mongo.log
Sau đó trỏ tới thư mục cài đặt mông trên ổ C: của mình đã cài đặt như đường link:
C:\Program Files\MongoDB\Server\3.2\bin
Và gõ lệnh sau :
mongod.exe --config C:mongodb\config.txt
Sau đó gõ lệnh sau để kiểm tra version : mongodb -version
Sau đó tới thư mục sau và gõ lệnh để chạy :mongod.exe –dbpath “C:\mongodb\data”
Chú ý : Từ lần sau khi muốn kết nối với Mongodb bằng Node.js hay PHP … bạn mở CMD và thự thi dòng lệnh trên
Các bạn tham khảo nội dung hướng dẫn dung Robomongo quản lý Mongodb: https://smartjob.vn/huong-dan-dung-robomongo-quan-ly-mongodb/
Tải source code tại đây:node_mongo
Tạo cấu trúc file thư mục như sau : folder : node_mongo file : conect_smartjob_mongo.js
Mở CMD và trỏ tới thư mục chứa file conect_smartjob_mongo.js gõ lệnh : npm install mongodb như hình dưới
Sau đó thì thư mục sẽ xuất hiện thêm folder:node_modules như hình vẽ
Nội dung file : conect_smartjob_mongo.js
//lets require/import the mongodb native drivers.
var mongodb = require('mongodb');
//We need to work with "MongoClient" interface in order to connect to a mongodb server.
var MongoClient = mongodb.MongoClient;
// Connection URL. This is where your mongodb server is running.
var url = 'mongodb://localhost:27017/test';
// Use connect method to connect to the Server
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
}
else
{
//HURRAY!! We are connected.
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('zips');
// Find some state
collection.find({'state': 'MA'}).toArray
(
function (err, result) {
if (err) {
console.log(err);
} else if (result.length) {
console.log(result);
//var result=result.length;
} else {
console.log('No document(s) found with defined "find" criteria!');
}
//Close connection
db.close();
}
);
}
});
Ở dòng 24 của file conect_smartjob_mongo.js như trên ta thấy điều kiện truyền vào lấy ra các bản ghi (theo cách nói của cơ sở dư liệu mysql ) ở đây là lấy ra các document có điều kiện là có state bằng ‘MA‘ và hiển thị lên cửa sổ lệnh cmd qua lệnh console.log sau đây là kết quả hiển thị được:
Ở cùng dòng đó ta có thể thay bằng các điều kiện khác để thực hiện truy vấn như:
– collection.find( ).toArray // lấy tất cả các kết quả trong collection
– collection.find({‘state’: ‘MA’,’pop’:9610}).toArray // thêm 1 điều kiện query nữa
– collection.find({‘address.zipcode’: 10075}).toArray // nếu có chứa các cặp field: value lồng trong cặp field:value
-collection.find({ pop:{$gt:9600} }).toArray // điều kiện lớn hơn với filed pop
-collection.find({ pop:{$lt:9600} }).toArray // nhỏ hơn
Làm tương tự và thực hiện lệnh thực thi file insert.js giống như ở trên : node insert.js file code insert.js như sau:
//lets require/import the mongodb native drivers.
var mongodb = require('mongodb');
//We need to work with "MongoClient" interface in order to connect to a mongodb server.
var MongoClient = mongodb.MongoClient;
// Connection URL. This is where your mongodb server is running.
var url = 'mongodb://localhost:27017/test';
// Use connect method to connect to the Server
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
}
else
{
//HURRAY!! We are connected. :)
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('zips');
//Create some document
var smartjob1 = {city: 'smartjob.vn', pop: 1242, state: 'MA', loc: [-72.936114, -72.936114]};
var smartjob2 = {city: 'mang tuyen dung hang dau viet nam', pop: 3442, state: 'MA', loc: [42.182949, 42.182949]};
// Insert some users
collection.insert([smartjob1, smartjob2], function (err, result) {
if (err) {
console.log(err);
} else {
console.log('Inserted %d documents into the "users" collection. The documents inserted with "_id" are:', result.length, result);
}
//Close connection
db.close();
});
}
});
Chú ý dòng 27 insert dữ liệu vào qua hai biến smartjob1,smartjob2.
và file update.js update thay đổi 1 thông số trong document
//lets require/import the mongodb native drivers.
var mongodb = require('mongodb');
//We need to work with "MongoClient" interface in order to connect to a mongodb server.
var MongoClient = mongodb.MongoClient;
// Connection URL. This is where your mongodb server is running.
var url = 'mongodb://localhost:27017/test';
// Use connect method to connect to the Server
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
}
else
{
//HURRAY!! We are connected. :)
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('zips');
collection.update({city: 'smartjob.vn'}, {$set: {city: 'smartjob.vn2'}}, function (err, numUpdated) {
if (err) {
console.log(err);
} else if (numUpdated) {
console.log('Updated Successfully %d document(s).', numUpdated);
} else {
console.log('No document found with defined "find" criteria!');
}
//Close connection
db.close();
});
}
});
Chú ý dòng 22 thay thế 1 document có thông số city = smartjob.vn và update city = smartjob.vn2
Xóa 1 document có trường (key) city= smartjob.vn2
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
// Connection URL. This is where your mongodb server is running.
var url = 'mongodb://localhost:27017/test';
// Use connect method to connect to the Server
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
}
else
{
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('zips');
// start
collection.deleteOne({ city : 'smartjob.vn2' }, function(err, result) {
if (err) {
console.log(err);
} else {
console.log('xoa thanh cong');
}
//Close connection
db.close();
});
//end
}
});
Ở các bài tiếp theo mình cũng giới thiệu cách kết nối Mongodb bằng node.js theo ODM hướng đối tượng băng Mongose . Chúc các bạn thực hành thành công mọi thắc mắc các bạn có thể liên hệ qua Skype : nguyenanhdung90.
For fun tí thôi, nhưng mà thực tế khi bạn hiểu được tại sao lại leak như vậy rồi thì lần sau bạn sẽ không dễ bị leak thế nữa, đúng không nào? Trong bài này, chúng ta lại tự ngã vào 1 leak hoàn toàn mới: Đó là leak trong singleton. Ồ nghe nó có vẻ nguy hiểm nhỉ. Nhưng mình cá khi đọc xong cái bài này các bạn lại bảo “ôi tưởng thế nào, dễ vãi lằn ” OK con dê, vậy hãy bóp mông em đồng nghiệp 1 cái rồi vào việc luôn nào.
Tuy nhiên tôi khuyên bạn chơi với terminal của MacBook cho nó pro nhé. Vì nếu bạn tải chay về bằng trình duyệt thì không thấy source code ở đâu đâu :v
Chơi với terminal đơn giản như sau:
Bạn gõ lệnh cd vào source code hôm trước bạn tải về: cd LeakMemorySample
2. Tiếp theo là gõ fetch để lấy source code mới nhất của tôi về: git fetch
3. Tiếp theo là bạn show toàn bộ branch trên repo của tôi bằng lệnh: git branch -a
Ở đây bạn sẽ thấy các branch sau: bai1 * bai2 master
Ví dụ bài này, tôi để hết source vào branch tên là bai2. Bạn chuyển sang source code bài 2 như sau: git checkout bai2
Sau khi branch bai2 được bôi đậm chuyển màu nghĩa là bạn đã thành công rồi đó. Còn nếu nó báo không thấy thì chứng tỏ bạn làm sai, hãy làm lại!
Singleton là gì?
Trước khi tạo được leak cho nó thì chúng ta hãy hiểu Singleton là gì đã nha? Nó là 1 cái khái niệm gì đó bằng tiếng Anh. Nhưng nếu hiểu theo nghĩa thuần Việt singleton là kỹ thuật tạo class mà chỉ khởi tạo được duy nhất 1 lần. Nghĩa là gì? Cùng xem ví dụ sau:
class AlertViewHandleLogic {
}
Tôi tạo 1 class như trên và hoàn toàn không có gì trong đó cả. và bạn mở project lên, vào file SingletonLeakViewController:
thì thấy nó chạy bình thường như hình. Tiếp theo tôi biến class AlertViewHandleLogic thành singleton như sau:
Chuyển class thường thành class Singleton
Dòng 11: Tôi đưa hàm tạo init thành private, nghĩa là không cho phép các lớp khác khởi tạo nó nữa.
Dòng 12: Tôi tạo 1 biến static shared để gọi hàm tạo tại chính nó, ok nó chạy được.
Khi tạo như này, bạn build lại và vào xem ở file SingletonLeakViewController thì kết quả như sau:
Nó báo lỗi đỏ chót, dịch ra nghĩa là hàm init được bảo vệ private – do đó bạn không thể gọi hàm tạo được ở đây. OK vậy tôi sẽ gọi lớp này như nào?
Tôi gọi nó qua biến shared. Lúc này do shared là biến static nên nó chỉ khởi tạo đúng 1 lần và biến này tồn tại mãi mãi trong class AlertViewHandleLogic. Vậy là bạn đã tạo được 1 class Singleton thành công
Vậy trong thực tế, chúng ta có hay gặp singleton không? Ồ rất nhiều luôn bạn nhé. Ví dụ Apple có 1 đoạn code trong UIKit như sau:
Thì biến shared ở trên chính là singleton nha. Ở ví dụ trên, khi tôi muốn lấy rootViewController thì tôi thông qua biến shared của lớp UIApplication để truy cập. Vì mỗi ứng dụng IOS thì cần 1 biến để lưu rootViewController và duy nhất thôi, cho nên họ tạo singleton cho nó. Quy tắc đặt tên cho singleton thường là shared, hoặc là getInstance() như thời Objective-C bạn nhé. Vậy là bạn biết được tác dụng của Singleton như nào rồi ha. Hãy nhớ cho mình chúng ta chỉ tạo Singleton khi:
Quản lý 1 biến hay class duy nhất chạy xuyên suốt ứng dụng
Static sẽ không được giải phóng cho đến khi ứng dụng giải phóng
Do vậy, cũng không thể lạm dụng Singleton hay static được đúng không nào? Nếu bạn hiểu đơn giản thì swift hay các ngôn ngữ lập trình khác có 2 loại biến cơ bản là dynamic và static. Dynamic thì khởi tạo runtime, và giải phóng khi lớp chứa nó hủy. Còn static thì khởi tạo luôn khi class đó được gọi init, và tồn tại xuyên suốt ứng dụng. Tuy nhiên nhiều trường hợp bắt buộc vẫn phải dùng static dù muốn hay không.
Trong ví dụ chúng ta làm sau đây, tôi muốn chương trình sẽ sau 1 khoảng thời gian nào đó hiển thị 1 màn hình alert lên view controller, bất kỳ ở đâu nó cũng show lên được ha. Và việc xử lý show đó do class AlertViewHandleLogic xử lý. Nghe hấp dẫn nhỉ? Trong thực tế có những ví dụ như bạn muốn sau 1 khoảng thời gian ứng dụng tự lock màn hình cũng có thể áp dụng, vân vân mây mây ha.
OK đọc đến đây thì cũng mệt rồi, nên hãy đi hát 1 bài rồi ta lại tiếp tục nha
2. Tạo singleton quản lý show alert
Cùng theo dõi đoạn code sau:
Xử lý logic show popup nhờ timer
Dòng 12, 13 tôi có 1 biến timer để tính sau 1 khoảng thời gian sẽ show alert ra
Dòng 23 đến 27, tôi tạo hàm để cứ sau 1 khoảng 5s nó sẽ gọi vào biến closure needShowAlertView. Mục đích biến này là bắn ra callback cho view controller nào được gán sẽ thực hiện action này.
Vào SingletonLeakViewController và theo dõi đoạn code sau:
Dòng 15, tôi khởi tạo biến singleton của class AlertViewHandleLogic
Dòng 16 tôi gán closure needShowAlertView và hiển thị 1 dòng chữ need show here
Dòng 20 tôi gọi hàm resetShowAlertTimer để timer hoạt động
Sau khi build tôi nhận được kết quả sau:
Vậy là cứ đều đặn 5s nó sẽ bắn ra callback để chúng ta có thể viết logic show cái alert này ra.
Tuy nhiên, vì hàm alertViewHandleLogic.resetShowAlertTimer() gọi rườm rà nên tôi sẽ đưa nó vào hàm init của AlertViewHandleLogic để không phải gọi mỗi khi tạo callback needShowAlertView.
Tuy nhiên có 1 vấn đề nghiêm trọng như sau:
Biến closure needShowAlertView thuộc lớp AlertViewHandleLogic, là class Singleton. Do đó khi biến này được gán ở SingletonLeakViewController thì chỉ có view này show alert thôi. Giả sử tôi gán vào SecondViewController thì nó không còn hoạt động ở SingletonLeakViewController. Ồ trong khi tôi muốn là nó phải hoạt động ở tất cả các viewcontroller cơ mà
Khó rồi nha, vì singleton chỉ là 1 và duy nhất, cho nên chúng ta không thể áp dụng kỹ thuật callback bằng closure này được. Giải pháp là gì?
3. Thay thế closure callback bằng observers
quát dờ heo? Ông vừa chỉ tụi tôi cái singleton rồi định chỉ thêm cái observers? Có nhiều quá không vậy? Ồ nếu bạn vừa ngáp ngủ thì thôi tạm thời làm cốc trà đá, hút điếu thuốc, ngắm em bán nước mông cong 1 lúc rồi lên tiếp tục sau nha.
OK rồi bây giờ ta sẽ tiếp tục nghiên cứu observers là gì nè?
Observers dịch theo nghĩa thuần Việt nhất là 1 cái quan sát viên. Mỗi khi timer tới 5s, thì nó sẽ phải báo cho tất cả những thằng view controller dùng nó phải show alert lên. Tất nhiên là view controller đang ở chế độ hiển thị nha. Kỹ thuật này dùng thế nào?
Đơn giản là bạn tạo 1 cái protocol ở cái class AlertViewHandleLogic, sau đó bạn tạo thêm các hàm add và remove các protocol này để sử dụng hay xóa việc lắng nghe protocol ở các view. Protocol có nhiệm vụ báo cho các view controllers.
Nói thì hay lắm, code xem nào Theo dõi đoạn code sau:
Dòng 11 đến 14, Ở đây tôi tạo protocol AlertViewHandleLogicDelegate có nhiệm vụ là bắn hàm needShowAlertView để show alert. Một số quy tắc viết protocol nếu bạn chưa nắm vững có thể xem tại đây ha.
Dòng 18 tôi làm 1 biến observers kiểu AlertViewHandleLogicDelegate nhằm lưu trữ các observer(quan sát viên) của các view controller muốn listen nó.
Tiếp tục xem đoạn code:
Dòng 43 đến 46, tôi tạo hàm add observer. Tôi có check nếu nó chưa được add thì mới thêm, có rồi thì bỏ qua không thêm nữa.
Dòng 51 đến 54 tôi viết hàm remove observer. Vì không thêm lặp cho nên tôi chỉ check phần tử đầu tiên nếu là thằng cần remove thì xóa ra khỏi list thôi.
Trong hàm bạn sửa lại 1 xíu như sau:
Mục đích là chạy 1 vòng for rồi notification cho tất cả những thằng đã add observer nha. Tôi comment cái callback bằng closure.
OK vậy là bạn đã chuẩn bị để chơi với observer rồi nha. Bây giờ bạn vào SingletonLeakViewController và code như sau:
Dòng 21 tôi add observer cho SingletonLeakViewController. Khi add như này thì nó sẽ lắng nghe được sự kiện sau 5s sẽ show alert.
Dòng 26 đến 29, vì tôi add cho nên class SingletonLeakViewController phải extend các hàm từ protocol AlertViewHandleLogicDelegate. Tôi show print để biết nó hoạt động.
Ở các view controller khác bạn làm tương tự.
Và kết quả chạy như sau:
Good job. Vậy là nó đã hoạt động
Nhưng khoan đã, ông vừa bảo leak cái mẽ gì trong singleton rồi cơ mà, sao giờ lại lan man ra đây? Ồ bây giờ tôi mới chốt issue nè. Leak memory đã xảy ra rồi đó!
Bây giờ bạn thêm dòng code sau để check SingletonLeakViewController giải phóng khi bấm back ra không:
Và nó hoàn toàn không nhảy vào! OMG tôi đã làm gì sai mà leak vậy
Rồi quay lại vấn đề, việc ta add 1 observer vào SingletonLeakViewController như sau:
AlertViewHandleLogic.shared.addObserver(self)
Thì view controller này đã tham chiếu mạnh tới class AlertViewHandleLogic. Nghĩa là lớp SingletonLeakViewController chỉ bị giải phóng khi observer trong AlertViewHandleLogic được giải phóng. Tuy nhiên AlertViewHandleLogic làm sao mà giải phóng được khi nó có hàm remove ở đâu đâu. Có bạn bảo bạn ơi sao bạn không đưa hàm remove như này:
Rất tiếc là nó đã không giải phóng thì không nhảy vào deinit, mà không vào thì không chạy remove observer được. Câu chuyện trở thành bế tắc rồi
Giải pháp của tôi như sau:
Tôi trick khi nhấn nút back ở UIBarButtonItem thì sẽ gọi vào hàm viewWillDisappear, tôi sẽ xóa ở đó:
Kết quả như sau:
Vậy là SingletonLeakViewController đã call được vào deinit, nghĩa là nó được giải phóng
Tuy nhiên tôi thấy thật sự đây cũng chưa phải là giải pháp thông minh nhất, vì bạn sẽ phải biết trick tùy trường hợp mà giải phóng chứ không phải lúc nào cũng là như tôi làm ở trên.
OK vậy là bài này cũng khá dài rồi, nếu bạn có giải pháp nào hay hơn của tôi vui lòng hãy comment cho tôi biết ở dưới bài này nhé.
Cảm ơn vì đã theo dõi tới đây và hi vọng bạn sẽ học được thêm nhiều kiến thức hay ho, thực tế qua bài này. Thanks all!
Tuần trước đã có bài viết về nextTick() trong Vuejs, nhân đây muốn giới thiệu qua cho anh em luôn về Vuejs Render Process. Hiểu process của Vue mới nâng tầm lên được. Chứ cứ v-if, v-else, v-show hoài mà không biết nó render như thế nào thì toang.
Sẵn sàng chưa nào?. Xúc ngay và luôn cho nóng nha!
Trước khi tìm hiểu về Vuejs Render Process, bắt buộc phải có kiến thức về DOM (Document Object Model). Anh em nào chưa biết có thể tìm hiểu qua bài viết này. Trước đây khi có thay đổi phía FE, một node trên DOM tree thay đổi sẽ kéo theo phải render lại toàn bộ tree.
Việc thay đổi trên toàn bộ cây DOM thật sự tệ. Chính vì vậy, Virutal DOM ra đời.
Tóm tắt nhanh như tốc độ crush tỏ tình như sau:
Virtual DOM giúp tăng perfomance khi thực hiện thay đổi trên screen
Luôn có sự so sánh những gì thay đổi giữa VirtualDOM và DOM trước khi cập nhật.
Virtual DOM được sử dụng cho các dự án sử dụng Vuejs
Thực chất, khi run build Vuejs Application
2. Vuejs Render Process
Cả một quá trình dài trước khi Virtual DOM được sinh ra và phản ánh lên DOM thật. Quá trình này bao gồm 4 bước (nói rõ ra thì Vuejs Render Process) bao gồm 4 bước chính. Để hiểu rõ hơn về quá trình render, bắt buộc phải nhớ kĩ các step đó như sau:
4 bước render process của Vuejs nằm ở đây.
Nguồn / Source: Medium
2.1 defineProperty
Bắt đầu một process dài cập nhật lên DOM tree luôn là defineProperty. Ở step này, một khi dữ liệu được cập nhật và xác nhận là có thay đổi, method defineProperty sẽ được goi.
Về mặt bản chất, defineProperty có vai trò như người thông báo. Nó luôn lắng nghe cho mỗi lần data thay đổi, thông báo tới watcher sự thay đổi về data tại component.
Dữ liệu A thay đổi -> defineProperty gọi function setter -> thông báo cho watcher.
Rõ ràng mà nói, step đầu tiên trong Vuejs Render Process là defineProperty, việc xác định được data nào thay đổi báo cho watcher là bước đi vô cùng quan trọng
2.2 Watcher
Watcher được khởi tạo cho từng component trên Vue, ngay lúc application được khởi tạo. Nhiệm vụ chính của watcher là cập nhật DOM và Virtual DOM. Tuy nhiên, Watcher chỉ update cây DOM tạm thời (immediately) sau khi setter thông báo cho nó biết.
2.3 Queue
Khi thằng watcher nhận được thông báo thay đổi, nó tự đưa nó vào một hàng đợi (Queue). Nguyên nhân là có nhiều data thay đổi cùng lúc. Để xử lý được hết cần đẩy vào hàng đợi (để xử lý từ từ).
Nếu cả title và message trên component được update data cùng lúc (sau khi gọi API thành công). Vue Watcher sẽ đẩytừng thay đổi đó vào Queue (thứ tự order theo các điều kiện đặc biệt)
2.4 nextTick
Cuối cùng là nextTick API (bạn nào chưa biết có thể tham khảo bài viết này). nextTick giúp consumed and flushed (dịch tạm là chấp nhận và đẩy ra) cây DOM thật. Mỗi watcher sẽ được cập nhật lên DOM thật từ Virtual DOM. Đây cũng là cách là phương thức watch trong Vuejs hoạt động.
Việc cập nhật từ Virtual DOM lên DOM được thực hiện tự động bởi Vue qua method run(). Tuy nhiên, cũng có thể tự gọi function này khi muốn cập nhật DOM ngay lập tức.
3. Kết luận
Tóm lại, Vuejs Render Process chỉ tóm gọn lại trong bức hình dưới đây. Mỗi quá trình đều có một chức năng và nhiệm vụ khác nhau. Tuy nhiên, chỉ đơn giản nhớ rằng:
data thay đổi -> gọi defineProperty -> báo cho watcher cái gì đã thay đổi
watcher trên từng component được tạo -> nếu có nhiều watcher -> đẩy vào queue
Lần lượt từng Queue gọi nextTick() API -> cập nhật lên DOM
Qua bài viết này, mong rằng các vị huynh đài có cái nhìn khách quan và rõ ràng hơn về Vuejs Render Process. Để làm việc, code được với Vuejs thì không cần hiểu tới render process. Nhưng để trở thành master, được gọi là Senior thì cần nắm rõ kiến thức này.
Ngoài hiểu sâu để nhớ lâu, những thông tin bài viết cũng cấp còn giúp fix những bug quái lạ khi làm việc. Biết khi nào nên set data thay đổi, khi nào nên cập nhật Virtual DOM lên DOM.
Bài viết được sự cho phép của tác giả Nguyễn Văn Trọng
Sau 6 tháng trời bỏ bê thì nay mình quay lại viết lách một chút. Đợt rồi có xuất bản sách nhưng thiết nghĩ những gì viết trong đó đều ở mức rất cơ bản. Không biết thì không làm được nhưng cho dù biết hết cũng chưa chắc giỏi được. Bởi vậy mình sẽ quyết định viết tiếp cho tới khi nào anh em chán đọc thì dừng. Chắc không ai chán tại viết hay thế kia mà (muahaaa, tự an ủi).
Vào luôn chủ đề chính, nay mình tản mạn một chút về các tố chất cần thiết để có thể theo nghề này.
– Kiên trì
– Tập trung
– Thích nghi
Có rất nhiều yếu tố để tạo nên thành công trong sự nghiệp mỗi người. Mình không tự nhận bản thân đã làm được gì to tát nhưng nếu không có những tố chất trên thì rất khó để đi xa được cho tới ngày hôm nay. Bởi vì nó quá cần thiết nên muốn chia sẻ ngay cho anh em.
Nếu chọn đích đến là BrSE thực thụ thì cần có 3 kỹ năng chính : Code, JP và kỹ năng mềm. Cái này mình đã nói nhiều lần, vậy nên cho dù xuất phát điểm làm NonIT-JPN2 +, IT + NonJP, hoặc không có cái gì hết … thì cũng tới đích được, thời gian dài ngắn tuỳ điểm bắt đầu cũng như nỗ lực từng người. Cuối cùng người kiên trì nhất mới là người tới đích.
Ví dụ như việc học tiếng Nhật. Hồi sinh viên cũng chỉ tình cờ đi phỏng vấn 1 công ty Nhật về trường tuyển và pass, cũng không hiểu sao lại được chọn với tỉ lệ 40/1000 sinh viên, chắc tại đẹp trai. Sau đấy là một khoảng thời gian vật lộn với biết bao cám dỗ (game, nhậu, bóng đá …) để theo lớp tới cùng, và mình là một trong 9 người còn lại của lớp đấy trong khi mấy bạn khác bỏ hết. Sau đấy dù cố thế nào thì hợp đồng bị huỷ do khủng hoảng, vậy là hơn 6 tháng công sức đổ biển. Sau đấy ra trường đi làm, trong khi các bạn khác bỏ luôn thì mình vẫn miệt mài đi học thêm JP ở các lớp buổi tối, rồi tham gia khoá BrSE cho tới lúc có thể nghe nói đọc viết tốt và sang được JP. Nhưng sau đấy lại phải học thêm vì nghe khách nói chả hiểu mie gì, nói năng lắp ba lắp bắp nhưng trước đấy cứ nghĩ mình ngon rồi. Tiếp tục mài dùi cho tới bây giờ vẫn vậy.
Đó chính là tính kiên trì. Vậy nên nếu bạn nào đang bơ vơ lạc lõng thấy học mãi không vào thì cứ so đi, mình mất gần 10 năm học, các bạn đã bỏ ra bao lâu thời gian mà ngồi đấy than ? Không chỉ tiếng Nhật, code và kỹ năng mềm nó cũng vậy, cứ đi tiếp thì dù nhanh chậm thế nào cũng sẽ tới đích.
Tập Trung
Tập trung có thể hay bị nhầm với kiên trì. Thực sự không phải. Có những người rất kiên trì học, học JP không ổn học tiếng Anh, học không xong chuyển qua tiếng Hàn, và cứ kiên trì trong việc “HỌC NGOẠI NGỮ” nhưng quên mất rằng do thiếu sự tập trung nên chả đi tới đâu, có thể đi du lịch vòng quanh thế giới, tới đâu chào hỏi bằng thứ ngôn ngữ đó có vẻ rất siêu phàm nhưng chừng đó không đủ để làm việc – họp hành – đàm phán.
Huyền thoại võ thuật Lý Tiểu Long có một câu nói : “Tôi không sợ người luyện tập 10.000 cú đá chỉ một lần mà chỉ sợ người thực hành một cú đá 10.000 lần”. Và chính câu nói này đã giúp mình rất nhiều qua năm tháng.
Trong việc học code cũng thế, kiên trì học năm này qua năm khác, C# học 3 tháng chuyển qua Java, rồi vài tháng sau nhảy qua Python, chưa hết, khi thấy người ta thi nhau học trí tuệ nhân tạo, blockchain thì lại bỏ ngang cái đang học tiếp tục lao vào cái mới. Cứ thế đến cuối cùng cái gì cũng biết sơ sơ nhưng chả thạo, mà không thạo rất khó dùng. Có những kỹ sư chỉ biết mỗi 1 ngôn ngữ họ vẫn sống rất tốt, lương rất cao vì họ cực kỳ tập trung trau dồi cái mình mạnh nhất. Cắt nghĩa 1 chút để các bạn đỡ hiểu nhầm, vì có lần mình đã khuyên nên học để biết thật nhiều ngôn ngữ, nhưng nên nhớ phải có một thứ nổi bật nhất, giỏi nhất chứ không phải biết cho rộng nhưng không cái nào dùng được.
Lúc đi phỏng vấn thường các bài thi code người ta sẽ cho mình chọn một ngôn ngữ tự tin nhất để giải đề, dù ngôn ngữ nào cũng sẽ cho ra cùng 1 kết quả. Đó chính là lúc dùng sự tinh nhuệ. Còn trong trường hợp một dự án sử dụng nhiều loại ngôn ngữ khác nhau ví dụ : Front-End dùng html+css+Javascript+Angular+Node, Back-End dùng Java Spring Boot, Bath dùng C++(để chạy cho nhẹ trong đồng bộ dữ liệu). Đây chính là lúc cần sự hiểu rộng.
Tóm lại phải tập trung để mang lại cho mình một thứ giỏi nhất nhưng đừng quên tìm hiểu rộng ra để hình dung được mọi thứ xung quanh.
Xưa lúc còn làm công ty cũ, ktx có phòng dành cho 2 người. Vừa tiết kiệm tiền nhà cho công ty, vừa có bạn cũng vui. Mình có ở cùng ông anh hơn 3 tuổi. Cũng là kỹ sư, onsite như nhau nhưng thấy anh khó ăn khó uống, chỉ ăn được đồ Việt không ăn đồ Nhật nên 6 tháng công tác toàn nấu cho ăn, sau đó mình chuyển chỗ, anh ăn mỳ tôm 2 tháng chịu không nổi nên xin về nước. Còn mình thì cuộc sống nhẹ nhàng, thích ăn gì ăn thích ngủ đâu ngủ, có bữa còn ngủ ở ga tàu đợi 5h sáng tàu chạy mới về nhà ngủ tiếp :))) đàn hát bóng đá bóng chuyền bơi lội cái gì cũng tham gia hoà mình vào. Ngày đi làm, tối đứng bếp nấu ăn ngon không thua gì các mẹ các chị. Việc nhiều làm nhiều, việc ít thì dành thời gian đọc cái này cái kia, rồi luyện kỹ năng viết lách. Khách dễ thì mình vui, gặp khách khó chửi bới om sòm thì mình coi như gặp “thầy”, họ dạy mình để sau này gặp khách bớt khó hơn sẽ coi là dễ. Đi nhậu, khách uống bia mình bia, họ rượu mình rượu, ăn cá sống cũng ăn, ngay cả thịt gà sống người ta làm susi cũng ăn thử cho biết. Không có gì phải ngại.
Trong suốt 10 năm làm nghề đã từng rất nhiều lần chuyển dự án, chuyển team, bộ phận và kể cả chuyển việc. Mình khá quen với việc thoát ra khỏi vùng an toàn và tập thích nghi với cái mới. Phải tập vì ban đầu cái gì cũng khó khăn, và đều vượt qua cả. Như con tắc kè, nhảy vào chỗ nào lập tức đổi màu theo môi trường xung quanh để tránh bị ăn thịt. Bản thân mình cũng thế, tự biết chả giỏi giang gì nên mỗi lần tới đâu luôn cố tránh sự khác biệt nhất có thể, và không bao giờ làm mình nổi bật để người ta ghét. Nhập gia tuỳ tục, vào dự án thấy team OT thấy bà nội thì cũng xắn tay làm tới khuya cùng mọi người, cuối tuần người ta tụ tập hát hò thì cũng sắp xếp đi theo, vài buổi cũng được. Chứ đừng có kiểu chảnh chó bảo trước giờ làm 4-5 công ty, hàng chục dự án chưa phải OT lần nào, tụi bây làm gì làm, cứ kẻng đánh 6h chiều xách mông về. Vậy chỉ có làm việc với dế, trừ khi anh có thực tài làm được những việc không ai làm được thì mới được hưởng những đặc ân không ai có. Nên nhớ kỹ điều này.
Kết
Tính thích nghi nó cũng liên quan tới kiên trì, vì việc tập làm quen cái mới bao giờ cũng cần thời gian. Còn kiên trì thì bắt buộc phải tập trung thì mới có hiệu quả. 3 tố chất này bổ trợ cho nhau giúp bạn đi tới đích. Không phải ai ban đầu cũng hội tụ đủ, nhưng nếu có quyết tâm cùng với thời gian trau dồi sẽ tự khắc tiến bộ. Và chắc chắn đường xa mấy cũng tới nơi, quan trọng có chịu đi hay không, vấp ngã có tự đứng dậy hay dọc đường thấy tiên nữ tắm suối quên luôn mục đích ban đầu ?
Lâu rồi mới viết lại, thấy vẫn giữ phong độ như trước. Tay nghề không hề mai một, và đặc biệt là độ tự tin không hề suy giảm, biết dở nhưng vẫn tự khen hay kkk.
Firebase Security là phần đứng giữa data và user trong ứng dụng. Không phải ngẫu nhiên mà Google thêm nội dung này vào Firebase. Trước đây, muốn thực hiện bảo mật hoặc phân quyền cho database cần nhiều thời gian để thực hiện.
Đối với các ứng dụng lớn và phân quyền phức tạp, người ta còn viết ra cả một service riêng để check permission. Đã thiết kế vậy đòi hỏi phải thiết kế DB, nếu chưa biết có thể tham khảo các bài viết về antipattern khi thiết kế DB tại đây.
Nhưng với các ứng dụng nhỏ, không quá phức tạp thì sao?. Đấy, lúc đấy Firebase Security trở nên đơn giản, gọn nhẹ.
Nhờ vào tính flexible (linh động) và independent (độc lập), firebase database nói chung và Firebase Security càng ngày càng hot. Cùng Kieblog tìm hiểu ngay 3 điểm cần chú ý nha.
Trường hợp mà muốn test function hay feature thì có ngay mode test, mở toang cho anh em vào thử nha. Nghe hấp dẫn vcl, cùng tìm hiểu thôi
1.1 Hoạt động như thế nào?
Theo như lý thuyết từ trang chủ thì:
Firebase Security Rules work by matching a pattern against database paths, and then applying custom conditions to allow access to data at those paths.
Firebase Security Rules hoạt động bằng cách khớp các mẫu (matching a pattern) trên các đường dẫn database và áp dụng các điều kiện cho phép truy cập data hay không trên các đường dẫn này
À, vậy là tất cả các đường dẫn trên database đều có thể thiết lập các rules này. Rules thường được việt với if else, tuy nhiên cũng có thể viết các điều kiện phức tạp hơn (tùy thuộc yêu cầu của ứng dụng đang dùng).
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
// Cho phép làm gì đó (read, write,...) nếu khớp điều kiện gì đó.
allow <<methods>> : if <<condition>>
}
}
2. Ba chức năng chính
2.1 Flexibility – linh hoạt
Linh hoạt ở đây không phải cho người khác dễ dàng thao tác mà là cho quản trị viên. Trường hợp muốn thay đổi rule gì đó trong security thì dễ dàng thêm thắt hay sửa đổi.
Write custom rules that make sense for your app’s structure and behavior. Rules use languages that allow you to leverage your own data to authorize access.
Viết các rules tùy biến phù hợp với cấu trúc và hành vi của ứng dụng. Rules còn được sử dụng từ các dữ liệu của chính bạn để quản lý việc truy cập.
Nói về độ flexible khi viết thì với đống Operator này là dư sức rồi nha
Tất nhiên Firebase Security viết ra không phải cho một loại ứng dụng. Chính vì vậy người dùng sẽ muốn thay đổi rule cho phù hợp với đặc thù ứng dụng của mình.
Cùng xem xét một số ví dụ sau đây nha:
// Trường hợp này lock toàn bộ document trên firestore
match /{document=**} {
allow read, write: if false;
}
// Chỉ các user đã authen mới được read, write
match /products/{productId} {
allow read: if request.auth != null;
}
2.2 Granularity – Chi tiết
Ở một số ứng dụng, việc phân chia user actions có thể chi tiết tới từng thao tác. User này có quyền được tạo data, trong khi user kia chỉ được xem.
Nhân thấy yêu cầu phân quyền chi tiết, Firebase Security chia read và write thành các thao tác chi tiết nhỏ.
In some situations, it’s useful to break down read and write into more granular operations. For example, your app may want to enforce different conditions on document creation than on document deletion. Or you may want to allow single document reads but deny large queries.
Trong một số tình huống, sẽ rất hữu ích nếu chia nhỏ read và write thành các thao tác nhỏ. Một số có thể chập nhận duy nhất một yêu cầu dọc dữ liêu, nhưng từ chối các truy vấn lớn hơn
service cloud.firestore {
match /databases/{database}/documents {
// Read chia thành get và list
match /cities/{city} {
// Applies to single document read requests
allow get: if <condition>;
// Applies to queries and collection read requests
allow list: if <condition>;
}
// Write thì chi tiết hơn, tạo, cập nhật và xóa
match /cities/{city} {
// Applies to writes to nonexistent documents
allow create: if <condition>;
// Applies to writes to existing documents
allow update: if <condition>;
// Applies to delete operations
allow delete: if <condition>;
}
}
}
Thay vì viết một service riêng rẽ kiểm tra các điều kiện để thực hiện phân quyền. Thằng Firebase Security chia nhỏ các thao tác đó ra. Phần thao tác truy cập database được phân quyền chi tiết theo các điều kiện if, dễ để kiểm soát
Việc phân quyền trong một file duy nhật với các condition còn dễ dàng cho việc maintainance sau này.
2.3 Independent security – Tính độc lập
Cái hay của Firebase Security là nó tách bạch phần phân quyền, bảo mật data riêng rẽ ra ngoài.
Mỗi database đều có một tab rules, cần gì cứ bay vào đó mà sửa thôi
Có thể một số ứng dụng phức tạp hoặc có các yêu cầu phân quyền đặc biệt không sử dụng. Nhưng đối với các ứng dụng đơn giản không có yêu cầu quá phức tạp về phân quyền có thể custom nhanh chóng phần này.
Because Rules are defined outside of your app (in the Firebase console or Firebase CLI), clients aren’t responsible for enforcing security, bugs don’t compromise data, and your data is always protected.
Bởi vì Rules được định nghĩa bên ngoài ứng dụng (trong Firebase console hoặc Firebase CLI), client không chịu trách nhiệm về vấn đề bảo mật, dữ liệu luôn được bảo vệ.
Tính độc lập của Firebase Security luôn được đánh giá cao. Dễ dàng custom và maintainance, đây là điểm cộng lớn cho Firebase. Một số đối thủ khác của Firebase đang cố gắng thực hiện tính năng tương tự.
Đây, introduction về Firebase security rules, vô cùng hữu ích
Giới thiệu đến bạn công nghệ JSF – một hợp phần trong hệ sinh thái JavaEE [1]. Phiên bản mới nhất hiện tại của JavaEE là phiên bản 7, phiên bản mới nhất của JSF (JavaServer Faces) là 2.2, được phát triển theo JSR344 [2]. JSF 1.0 được phát hành lần đầu vào ngày 11/03/2004, dùng để xây dựng các hợp phần (component) để xây dựng giao diện đồ họa người dùng (GUI) cho ứng dụng Java web. Các công nghệ WinForm, ZK Framework, GWT, Vaadin, JavaFX đều sử dụng cơ chế hợp phần (component) lắp ghép lại với nhau, JSF cũng vậy. Khi sử dụng JSF, bạn có thể khai thác tính năng AJAX phong phú, đơn giản, dễ dùng.
JavaEE là bộ đặc tả do Oracle cùng cộng đồng cùng xây dựng, JSF là công nghệ nằm trong bộ JavaEE do đó JSF là một công nghệ tiêu chuẩn và được hỗ trợ chính thức. JSF chạy được trên các JavaEE-compliance application server ( máy chủ tuân thủ bộ tiêu chuẩn JavaEE, như Oracle WebLogic, IBM WebSphere, GlassFish, v.v..).
Có nhiều JSF framework cho bạn lựa chọn
Hiện tại công nghệ JSF có rất nhiều framework, đó là:
Mojarra JavaServer Faces (cung cấp bởi Oracle [3])
PrimeFaces ( cung cấp bởi PrimeTek – một công ty phần mềm Brazil [4])
RichFaces (cung cấp bởi JBOSS – Redhat [5])
MyFaces Trinidad (cung cấp bởi Apache Software Foundation [6])
OmniFaces (được phát triển chính bởi Arijan Tijims, Bauke Scholtz, Jan Beernink [7])
ICEfaces (phát triển bởi ICEsoft [8])
Có vài framework nữa tuy nhiên không phổ biến nên chúng tôi không liệt kê thêm. Các JSF framework kể trên đều có phiên bản miễn phí. Thực tế sử dụng trên thế giới, cũng như tại Việt Nam, trong các JSF framework kể trên, thì phổ biến hơn cả là PrimeFaces, sau đó đến RichFaces.
Ứng tuyển các vị trí việc làm Java lương cao trên TopDev
JSF là ca nhạc, PrimeFaces là ca sỹ
Tác giả bài viết cố gắng trả lời cho bạn đọc một câu hỏi phổ biến khiến lập trình viên bối rối. Sự khác nhau giữa JSF và RichFaces là gì? Sự khác nhau giữa ICEfaces và JSF là gì?
Câu nói trên là cách dễ nhất để giải thích cho bạn về sự khác nhau giữa JSF và hàng loạt framework JSF đang tồn tại. JSF là công nghệ, còn PrimeFaces là sản phẩm cụ thể hóa công nghệ JSF. Bạn hãy liên tưởng, có công nghệ sản xuất động cơ đốt trong 4 thì, và Honda Air Blade là một sản phẩm cụ thể hóa công nghệ đó. Nếu JSF là ca nhạc, thì RichFaces, ICEfaces cũng là ca sỹ, v.v..
JSF khá dễ dùng, linh hoạt. Giả sử SmartJob sử dụng PrimeFaces để làm biểu mẫu nhập thông tin về công việc (job), các trường và loại component tương ứng sẽ là:
Các nhãn: label
Tên công việc: textbox
Công ty tuyển dụng: drop-down list (combo box)
Nội dung tuyển dụng: textbox
Ngày bắt đầu đăng tuyển: calendar
Ngày kết thúc đăng tuyển: calendar
Nút bấm gửi thông tin lên server: Button (type = submit)
Công nghệ JSF có nhiều ưu điểm: thời gian phát triển ứng dụng nhanh mà vẫn hỗ trợ AJAX. JSF có nhược điểm, JSF dựa trên cơ chế stateful component (tương tự cơ chế của ASP.NET WebForm) nên có nhược điểm là dung lượng request, response gửi qua lại giữa client-server lớn, dẫn đến thời gian giao tiếp giữa 2 phía sẽ lâu. Bản chất của HTTP là stateless , trong khi JSF cố giữ trạng thái các component trong ứng dụng bằng cơ chế stateful của component.
Ghi chú
[1] http://www.oracle.com/technetwork/java/javaee/overview/index.html
[2] JSR (Java Specification Request): Yêu cầu đặc tả (công nghệ cụ thể nào đó trong) Java
[3] https://javaserverfaces.java.net/
[4] http://www.primetek.com.tr/
[5] http://richfaces.jboss.org/
[6] http://myfaces.apache.org/trinidad/
[7] http://omnifaces.org/
[8] http://www.icesoft.org/java/home.jsf
Bài viết được sự cho phép của tác giả Nguyễn Văn Trọng
Dạo gần đây mình hay nhận được nhiều câu hỏi của các bạn sinh viên và một số bạn trẻ đang theo nghề lập trình. Nội dung xoay quanh chuyện cơm áo. Vì thấy nó rất chính đáng nên mình mới viết bài này để giải đáp thắc mắc. Trên thực tế, sau khi ra trường đi làm thì ai cũng vậy, theo đuổi đam mê là một chuyện nhưng ít ra phải đủ ăn. Đi làm vài năm dành dụm chút tiền, chưa tính đến chuyện phụng dưỡng cha mẹ nhưng chí ít cũng phải đủ cưới vợ.
Mình vẫn còn nhớ như in hồi 2012, mới bập bẹ bước vào nghề BrSE thì cũng là thời điểm lên xe bông. Vì chưa tích cóp được gì nhiều nên ngửa tay xin ông anh trai. Cũng may anh mình lúc đó còn đang độc thân, đi xkld Hàn nên có dư được chút. Nghĩ lại thì hơi kỳ kỳ, các bạn đừng có vậy nghen, tự thân tự lực vẫn tốt hơn “gọi trợ giúp” người thân.
Quay lại chủ đề chính, xu hướng gia công phần mềm cho Nhật. Những ai đang hoặc có ý định học tiếng Nhật thì đây là bài viết đáng để các bạn bỏ 5 phút ra nghiền ngẫm.Lịch sử gia công phần mềm
Thời điểm những năm 80-90 Các doanh Nghiệp của Nhật như Hitachi, Fujitsu, Toshiba, NEC, Sony … đều có bộ phận Software riêng. Họ tự xây dựng hệ thống thông tin cho mình. Sau này đến khoảng đầu 2000 trở đi thì xu hướng khoán ngoài (outsource) mới bắt đầu nở rộ. Lý do chính là Nhật vào thời kỳ này đã qua thời hoàng kim, các doanh nghiệp không còn giữ mức lợi nhuận khủng bố để nuôi bộ máy IT riêng. Họ buộc phải giữ lại những nhân sự chủ chốt chỉ để nghiên cứu giải pháp. Phần việc triển khai thì có 2 hướng. Đối với các dự án liên quan tính bảo mật cao, quyết định thành bại của tập đoàn thì thông thường sẽ do nội bộ làm từ A->Z. Hoặc có thể thuê các công ty uy tín trong nước để đảm bảo an toàn, không bị rò rỉ tài liệu hay ý tưởng. Còn những việc khác sẽ khoán ngoài mà chủ yếu là Ấn Độ, Trung Quốc. Điều này nhằm mục đích tiết kiệm chi phí tối đa, đồng thời mang lại hiệu quả cao.
Thời gian đầu thì Ấn độ là mạnh nhất, sau đấy Trung Quốc mới nhảy vào và dần chứng minh được tuyệt chiêu “lấy thịt đè người”. Đến giữa những năm 2000 thì Trung Quốc vượt lên thành đối tác số 1, thời điểm này Việt Nam mới manh nha nhúng tay vào. Bạn sẽ khá bất ngờ khi 1 người Ấn hay Việt lấy N2 trong vòng 1 năm, nhưng với dân Tàu thì chuyện N2 trong 6 tháng không có gì ghê ghớm. Bởi vậy có những dự án yêu cầu vài nghìn người N2-N3 thì các doanh nghiệp ở Đại Liên, Thượng Hải hay Thiên Tân đều có thể chiến được.
Những năm 2010 thì bắt đầu xu hướng dịch chuyển từ TQ sang các nước đông nam á trong đó Việt Nam được ưu tiên. Ngoài Việt ra còn có Myanma, Thái, Phi, Bangladesh. Nhưng các bác JP vẫn like dân ta, 1 phần vì rẻ, 2 là dễ bảo, thứ 3 code trâu. Còn về lý do dịch chuyển này nói ra đây hơi dài dòng, hẹn 1 bài khác vậy.
Thị Trường Gia Công Phần Mềm Nhật Hiện Nay
Vì tính tiết kiệm cộng với hiệu quả nên thì trường outsource vẫn phát triển mạnh trong vài năm gần đây. Các công ty liên tục mọc lên như nấm sau mưa. Có rất nhiều BrSE mà mình biết sau vài năm chinh chiến đã ra lập công ty riêng và khá thành công. Các bạn tham khảo danh sách Outsource Company List bên dưới.
Mình sẽ cắt nghĩa một chút vì sao lại tiết kiệm, vì sao lại hiệu quả. Về chi phí, nếu nuôi bộ phận IT đông người thì việc trả lương thưởng sẽ ngốn kha khá, trung bình 1 người tầm 50 man (~100 triệu)/tháng, 1000 người thì … nhiều quá tính không nổi. Chưa kể cơ chế tuyển dụng trọn đời ở Nhật, tức là chỉ có tuyển chứ hiếm khi sa thải, nghĩa là nuôi cả đời. Trong khi outsource thì hết dự án là thôi, trả 1 cục tiền lấy sản phẩm cái rẹc … xong.
Còn về tính hiệu quả, người chưa làm với Nhật nhiều mà chỉ “nghe nói” thường hay thần thánh hoá kỹ sư JP. Thực ra họ có các kỹ sư rất giỏi, rất sáng tạo nhưng tỉ lệ chỉ chiếm phần nhỏ. Hầu hết các kỹ sư IT đều ở mức biết việc, cẩn thận tỉ mỉ chứ rất yếu khoản học cái mới. Vậy nên đối với các dự án công nghệ tiên tiến thường họ chỉ nghiên cứu giải pháp, còn việc coding giao cho các doanh nghiệp Việt Nam sẽ hiệu quả hơn. Dân IT của nước mình kỹ thuật được đánh giá khá cao, chỉ thua Ấn vs Trung chút xíu.
Xu Hướng Tương Lai (5 – 10 Năm Nữa)
Có 3 xu hướng chính :
Tuyển người giỏi vào làm SE. Hiện có nhiều công ty như Rakuten, Line (chat), Microsoft JP, Recruit jp… hay tuyển kỹ sư chất lượng cao từ các nước khác. Yêu cầu là giỏi tiếng Anh/Nhật và kỹ thuật đỉnh. Profile họ sẽ refer từ nhiều nguồn, trong đó có Github hay các dự án mã nguồn mở. Ngoài ra sẽ test lúc phỏng vấn qua Skype.
Tiếp tục khoán ngoài các dự án phần mềm. Những công ty IT liên doanh Việt Nhật hoặc VN mà làm chuyên cho thị trường Nhật sẽ tiếp tục mở rộng. Hiện tại theo ước tính thì số lượng nhân viên mảng JP đang khoảng trên dưới 20 ngàn người và sẽ tiếp tục tăng.
Mua bán – sát nhập : Ngoài việc tự mở các chi nhánh tại Việt Nam thì các công ty JP để tránh rủi ro sẽ bỏ tiền ra mua đứt luôn các công ty đang hoạt động. Như vậy sẽ đỡ mất công xây dựng, tránh được các sự cố không đáng có hoặc khó khăn trong việc hoà nhập văn hoá. Vì thông thường sau khi mua/sát nhập, bộ máy cũ hầu như giữ nguyên. Vẫn hoạt động trên nguyên tắc mẹ con nhưng việc điều hành hoạt động vẫn theo tập quán như trước và chỉ thay đổi về mặt chiến lược.
Kết
Các bạn đang theo mảng JP hoặc sắp sửa học tiếng Nhật đọc xong hy vọng là đỡ hoang mang. Thêm 1 yếu tố nữa mà các bạn sẽ yên tâm là dân số Nhật hiện đang già dần. Số liệu dự đoán trong vài chục năm nữa sẽ giảm kinh khủng. Các tập đoàn lớn dày công gây dựng lên nên không dễ gì họ để suy yếu chỉ vì thiếu nhân lực trong nước.
Ngày nay các bác đều có xu hướng toàn cầu hoá. Không chỉ là trong việc kinh doanh mà ngay cả nhân sự cũng vậy. Chuyện cán bộ cấp cao họ có tỉ lệ ngầm 20 – 50 % buộc phải là người nước ngoài. Cái này hơi xa vời so với tầm hiểu biết hạn hẹp của mình. Còn riêng chuyện dev thì hiện tại ngồi quanh mình không ít người Trung Quốc, Myanma, Bangladesh, Ấn độ.
Thêm cái cuối cùng, nghành công nghiệp phim *** của Nhật thì khỏi nói rồi, nên lỡ học JP mà không dùng được cho công việc thì chí ít là xem phim không cần sub . Mấy đoạn hội thoại đầu phim cũng hay và “nhân bản” lắm.
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Khi làm việc với switch statement, các bạn sẽ hay gặp những vấn đề sau:
Vấn đề thứ nhất là chúng ta quên khai báo break statement: điều này có nghĩa code chúng ta sẽ không thoát khỏi switch statement sau khi match case đầu tiên mà sẽ thực thi tiếp những case tiếp theo, ví dụ như:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
switch (a + b) {
case 2:
System.out.println("Khanh");
break;
case 3:
System.out.println("Huong Dan Java");
case 4:
System.out.println("Lap Trinh Java");
default:
System.out.println("Hello");
break;
}
}
}
Kết quả:
Vấn đề thứ hai là nếu các bạn muốn trong trường hợp giá trị của biến với các giá trị khác nhau a và b thì xử lý cùng một đoạn code. Chúng ta sẽ viết code như sau:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
switch (a + b) {
case 2:
System.out.println("Khanh");
break;
case 3:
System.out.println("Huong Dan Java");
break;
case 4:
System.out.println("Huong Dan Java");
break;
default:
System.out.println("Hello");
break;
}
}
}
Hoặc:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
switch (a + b) {
case 2:
System.out.println("Khanh");
break;
case 3:
case 4:
System.out.println("Huong Dan Java");
break;
default:
System.out.println("Hello");
break;
}
}
}
Những cách viết này bất tiện vì duplicate code hoặc chúng ta đôi khi không control được nên đặt break statement ở chỗ nào.
Vấn đề thứ ba là nếu các bạn muốn sử dụng switch statement để determine giá trị của một biến nào đó, các bạn phải viết như sau:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
String message = "";
int n = 1;
switch (n) {
case 1:
message = "Khanh";
break;
case 2:
message = "Huong Dan Java";
break;
case 3:
message = "Lap Trinh Java";
break;
default:
break;
}
System.err.println(message);
}
}
Để giải quyết những vấn đề trên, từ Java 12, các bạn có thể viết switch statement với những cải tiến như sau:
Đối với vấn đề thứ nhất, Java 12 hỗ trợ chúng ta cách viết switch statement giống như lambda expression, ví dụ như:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
switch (a + b) {
case 2 -> System.out.println("Khanh");
case 3 -> System.out.println("Huong Dan Java");
case 4 -> System.out.println("Lap Trinh Java");
default -> System.out.println("Hello");
}
}
}
Kết quả:
Đối với vấn đề thứ 2 thì các bạn có thể viết lại switch statement như sau:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
switch (a + b) {
case 2 -> System.out.println("Khanh");
case 3, 4 -> System.out.println("Huong Dan Java");
default -> System.out.println("Hello");
}
}
}
Kết quả:
Còn đối vấn đề thứ ba thì bây giờ Java 12 đã hỗ trợ chúng ta return lại value với switch statement như sau:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int n = 1;
var message = switch (n) {
case 1 -> "Khanh";
case 2 -> "Huong Dan Java";
case 3 -> "Lap Trinh Java";
default -> "Hello";
};
System.out.println(message);
}
}
Kết quả:
Nếu các bạn muốn thêm code để xử lý business logic trong mỗi case của switch statement thì các bạn cần upgrade lên Java 13, sau đó thêm code và sử dụng từ khóa yield để return lại giá trị mà mình mong muốn. Ví dụ như sau:
package com.huongdanjava;
public class SwitchExample {
public static void main(String[] args) {
int n = 1;
var message = switch (n) {
case 1 -> "Khanh";
case 2 -> "Huong Dan Java";
case 3 -> "Lap Trinh Java";
default -> "Hello";
};
System.out.println(message);
}
}
Những kinh nghiệm từ quá trình tự phát triển của một Developer (Phần 1)
Tác giả: Victor Cassone
Con đường tự học và trưởng thành của một nhà phát triển luôn khó khăn và đầy bất trắc. Không có một đường thẳng nào từ người mới thành lập trình viên đến nghề nghiệp. Vì điều này, tôi tin rằng tất cả các nhà phát triển tự học đều có những câu chuyện hay ho để chia sẻ đến với mọi người.
1. Tập trung vào quy trình học tập và làm việc
Khi mới bắt đầu học lập trình, tôi chưa bao giờ có ý định lập nghiệp từ nó. Tôi chỉ muốn tạo ra một ứng dụng cụ thể và chân thật mà thôi. Cuộc hành trình của tôi bắt đầu khi tôi là sinh viên năm cuối đại học. Tôi vừa đọc xong tiểu sử của Richard Branson và có lẽ đã đọc quá nhiều TechCrunch. Tôi được bơm đầy năng lượng về mảng kinh doanh.
Một ngày nọ, một ý tưởng về việc xây dựng ứng dụng đã xuất hiện trong đầu tôi. Tôi nhanh chóng vẽ ra viễn cảnh về những thứ lớn lao tiếp theo mà mình có thể làm được. Tôi bị cuốn hút bởi ý tưởng và ngừng chú ý đến các bài giảng. Sự hào hứng của tôi đối với ý tưởng ứng dụng nhanh chóng phát triển đến mức tôi cảm thấy mình cần phải hành động.
Nhưng có một vấn đề lớn, ý tưởng của tôi là một ứng dụng dành cho thiết bị di động và tôi không biết bất kỳ ai có thể xây dựng các ứng dụng dành cho thiết bị di động. Tôi là một sinh viên đại học không có kinh nghiệm lập trình, kinh doanh hoặc thiết kế đang cố gắng học Android để có thể xây dựng một ứng dụng di động phức tạp. Tôi đoán đó là những gì bạn nhận được khi kết hợp một ý tưởng lớn với cỗ máy cường điệu của Thung lũng Silicon.
Tôi đã mua một vài cuốn sách về phát triển Android và giành vô số giờ trong phòng để cố gắng dán các ứng dụng của mình lại với nhau. Tôi không quan tâm ứng dụng hoạt động như thế nào, tôi chỉ muốn có một sản phẩm hoàn chỉnh.
Thời gian trôi qua và ứng dụng biến thành một Frankenstein của mã sao chép và dán. Ứng dụng không có nhiều tính năng và nó hầu như không chạy mà không gặp sự cố. Cho đến khi vô tình tham gia một lớp Khoa học Máy tính, tôi mới nhận ra rằng mình nên tập trung hơn vào việc thực sự cố gắng học phát triển phần mềm.
Không có khả năng lập trình khiến tôi từ bỏ ý tưởng ứng dụng ban đầu. Tôi nhận ra rằng tôi sẽ không làm nên điều lớn lao tiếp theo, ít nhất là hiện tại thì vẫn chưa. Theo thời gian, tôi quyết tâm và lên kế hoạch cho bản thân để có thể học tập nghiêm túc hơn. Tôi bắt đầu thích lập trình và cuối cùng bắt đầu sự nghiệp của một nhà phát triển phần mềm.
Ý tưởng về việc xây dựng ứng dụng lớn của tôi đã làm lãng phí một khoảng thời gian dài. Nhưng may mắn là tôi đã tìm thấy được sự tập trung ở những phút cuối. Khi bạn quá tập trung vào kết quả cuối cùng, bạn sẽ bắt đầu đi những con đường tắt. Các lối tắt có thể dẫn đến một số tiến bộ trong ngắn hạn nhưng về lâu dài, việc thiếu sót các kiến thức chuyên môn cơ bản sẽ xảy ra.
Điều quan trọng cần nhớ là khi bắt đầu học bất cứ điều gì mới đều sẽ yêu cầu bạn cần hoàn chỉnh ngay từ những bước nhỏ nhất. Mỗi bước đều sẽ đòi hỏi bạn phải cẩn thận và chuyên tâm nhiều hơn. Học những điều mới cũng giống như xây một ngôi nhà. Bạn bắt đầu với nền tảng trước khi bắt đầu quá trình xây dựng. Nếu nền móng bị lỗi, tất cả mọi thứ sớm muộn sẽ sụp đổ.
Đôi khi việc xây dựng một nền tảng vững chắc đòi hỏi bạn phải chậm lại. Không có gì phải xấu hổ khi đi chậm. Những người hiểu những điều cơ bản lần đầu tiên sẽ đi trước những người phải quay lại và học lại chúng. Thật sự tập trung vào quá trình làm việc sẽ giúp ích rất nhiều cho quá trình làm việc của bạn.
Khi tôi đang xây dựng ứng dụng của mình, Stack Overflow đã trở thành “người bạn” đồng hành xuyên suốt với tôi. Bất cứ khi nào tôi gặp khó khăn, tôi sẽ cố gắng cùng nhau tạo ra một câu hỏi hoàn hảo để hỏi cộng đồng Stack Overflow. Tôi trả lời trung bình một vài câu hỏi mỗi ngày.
Tôi sẽ dành một khoảng thời gian để rà soát trang web và cố gắng tìm một đoạn code chính xác có thể khắc phục các vấn đề mà tôi gặp phải. Khi tôi tìm thấy câu trả lời phù hợp, tôi sẽ sao chép và dán vào codebase của mình và cố gắng làm cho nó hoạt động với code hiện có của tôi. Tôi dành một ít thời gian để cố gắng hiểu code mà tôi đã thêm.
Quá trình này diễn ra trong một thời gian cho đến khi tôi tìm hiểu và nhận ra những sai sót trong phương pháp của mình. Stack Overflow vừa là cơ hội nhưng thực tế cũng là thách thức. Nó rất tốt trong việc giúp bạn giải quyết vấn đề, tuy nhiên, nếu không cẩn thận, bạn có thể nhanh chóng trở nên phụ thuộc vào trang web.
Đôi khi trang web giải quyết vấn đề quá tốt. Nó tạo ra một cảm giác tự tin sai lầm có thể dẫn đến đau đầu hơn trên đường. Stack Overflow chỉ cho bạn cách bắt một thứ gì đó hoạt động nhưng nó thường không cho bạn biết rõ ràng tại sao nó hoạt động. Hiểu được vấn đề như thế nào là quan trọng. Các lỗi cần được sửa và code cần chạy là những gì… Hiểu rõ lý do tại sao điều gì đó hoạt động sẽ giúp bạn áp dụng lại kiến thức trong tương lai.
Sao chép và dán code từ Stack Overflow giống như có ai đó cho bạn một con cá. Trong khi đó, việc hiểu rõ lý do tại sao một đoạn code hoạt động giống như dạy chúng câu cá. Không có gì sai khi sao chép và sử dụng code đó. Tất cả chúng ta đều làm được. Nó chỉ là một vấn đề khi nó cản trở sự phát triển của bạn với tư cách là một nhà phát triển.
Điều tôi đã phải học một cách khó khăn là không thể học được bất cứ điều gì nếu các câu trả lời liên tục được đưa ra cho bạn. Không có đường tắt trong quá trình học tập. Khi bạn gặp khó khăn, hãy cố gắng giải quyết vấn đề mã hóa ít nhất một vài lần trước khi truy cập Google. Khi sử dụng những đoạn code có sẵn, hãy dành một chút thời gian để cố gắng hiểu đoạn code trước khi tiếp tục.
3. Tìm hiểu sự trợ giúp từ những người có kinh nghiệm
Điều đầu tiên tôi làm sau khi quyết định học lập trình là mua hai cuốn sách phát triển Android. Khi bắt đầu, tôi theo sát các bài tập trong sách và làm việc thông qua các dự án ví dụ. Tuy nhiên, tôi nhanh chóng thất vọng với tiến độ mà tôi đang đạt được sau cuốn sách và quyết định bắt đầu tìm hiểu cách lập trình cho riêng mình.
Tôi đã giành vô số giờ ngồi một mình trong phòng để cố gắng tìm ra các vấn đề lập trình đơn giản. Tôi bị mắc kẹt với các dòng code mới và không hề cảm thấy mình đang tiến bộ. Tôi bế tắc một cách bất lực và cuộc sống của tôi là một hỗn hợp của sự nghi ngờ, thất vọng và cảm giác lạc lõng.
Để làm cho vấn đề tồi tệ hơn, tôi có một ý tưởng sáng tạo là bắt đầu sử dụng một C library khổng lồ có tên là FFMPEG. Ứng dụng của tôi cần để chỉnh sửa video, vì vậy tôi nghĩ rằng việc sử dụng chức năng mạnh mẽ của thư viện là một ý tưởng hay.
Đó không phải là bước đi thông minh nhất của tôi, vì vào thời điểm đó, tôi hầu như không thể làm cho ứng dụng Android của mình hoạt động. Tôi đã lãng phí rất nhiều thời gian để cố gắng đọc code C và tìm ra cách tôi có thể sử dụng nó trong ứng dụng của mình. Tôi đã đấu tranh để thậm chí nhập thư viện vào dự án Android của mình.
Sau nhiều giờ không đi đến đâu, cuối cùng tôi đã chán nản và từ bỏ thư viện. Cùng khoảng thời gian với sự cố FFMPEG, tôi đã đăng ký một lớp Lập trình hướng đối tượng. Nhiệm vụ đầu tiên là xây dựng một chương trình Blackjack. Tôi đã tự học lập trình được 5 – 6 tháng cho đến thời điểm này và tôi cảm thấy tự tin với kỹ năng của mình. Tôi đã hoàn thành bài tập và cảm thấy hài lòng về công việc của mình. Nhưng, không mất nhiều thời gian để cảm giác đó biến mất. Toàn bộ chương trình của tôi được viết theo một phương pháp rất lớn. Mọi người khác trong lớp đều có thể nhận ra rằng chương trình cần được tách thành các lớp. Nó không thật sự tốt!
May mắn thay, bài tập trên lớp và sự hướng dẫn từ giáo viên đã cho phép tôi bước ra khỏi ứng dụng Android và suy ngẫm về khả năng lập trình của mình. Tôi bắt đầu coi trọng việc học và khắc phục mong muốn của mình là tạo ra một ứng dụng hoàn chỉnh.
Bây giờ tôi nhận ra rằng nếu tôi chỉ nói chuyện với một nhà phát triển có kinh nghiệm trong những ngày đầu đó, họ sẽ thấy những gì tôi đang làm. Tôi có thể đã thiết lập các ưu tiên của mình một cách thẳng thắn, và nói lên một điều gì đó có ý nghĩa với tôi. Họ sẽ giúp tôi sửa chữa con đường của mình khi tôi đi vào những ngõ cụt vô ích (như cố gắng làm việc với FFMPEG).
Có rất nhiều cách tôi có thể làm để tìm kiếm sự trợ giúp. Tôi cố gắng tìm một giáo sư / sinh viên tại trường đại học có kinh nghiệm về Android hoặc tìm đến cộng đồng địa phương để được giúp đỡ. Tôi cũng có thể đã thử tìm một cộng đồng Android trực tuyến.
Các nhà phát triển có kinh nghiệm giống như một chiếc la bàn. Họ sẽ không đưa bạn đến đích nhưng họ sẽ đảm bảo bạn được chỉ dẫn đúng hướng. Sự giúp đỡ của họ có thể sẽ tạo nên sự khác biệt giữa thành công và thất bại. Đảm bảo rằng bạn tìm kiếm hướng dẫn ở bất cứ nơi nào bạn có thể tìm thấy. Nó sẽ giúp bạn tiết kiệm thời gian và hạn chế sự thất vọng trên chặng đường làm việc của mình.
Hi vọng những thông tin trên đây sẽ giúp bạn nắm được các vấn đề cơ bản khi bắt quá trình tự phát triển của bản thân. Đón xem phần tiếp theo cũng TopDev nhé!
Bài viết được sự cho phép của blogchiasekienthuc.com
Các tùy chỉnh mặc định của GeoGebra phần lớn thì đã phù hợp với đại đa số người dùng rồi. Tuy nhiên, vẫn có một số trường hợp ngoại lệ, ví dụ như là:
Mỗi lần dựng lên một đối tượng mới (điểm, đoạn thẳng, đường thẳng, …) thì tên của đối tượng này sẽ tự động được tạo và hiển thị trên Graphics.
Tên điểm có kích thước khá nhỏ.
Giao diện sử dụng là tiếng Anh …
Và mỗi lần như vậy, chúng ta đều phải tùy chỉnh lại => điều này tốn không ít thời gian, công sức và còn nhàm chán nữa. Chính vì vậy mà trong bài viết này, mình sẽ hướng dẫn cho các bạn cách thiết lập lại GeoGebra để sử dụng được hiệu quả hơn.
Các thao tác tùy chỉnh thông thường chỉ tự động lưu lại trong cửa sổ hiện tại. Vậy nên, nếu bạn muốn lưu lại cho các lần sử dụng tiếp theo thì bạn cần phải vào Options => chọn Save Settings để cài đặt.
#1. Không hiển thị tên đối tượng mới
Như đã giới thiệu ở trên, mỗi lần dựng một đối tượng mới (điểm, đoạn thẳng, đường thẳng, …) thì tên của đối tượng này sẽ tự động được tạo và hiển thị trên Graphics.
Thường thì chúng ta rất ít sử dụng những tên này, vì vậy nên tùy chỉnh ẩn ngay ban đầu chứ không phải tạo xong rồi mới ẩn một cách thủ công nữa.
Thực hiện: Chọn Options => chọn Labeling => chọn No New Objects để ẩn tên của các đối tượng.
#2. Cách chỉnh cỡ chữ lớn hơn trong GeoGebra
Theo mặc định thì tên của điểm có cỡ chữ khá là nhỏ, vì vậy nếu bạn trình chiều bằng Smart Tivi thì nhiều khả năng là học sinh ngồi ở những dãy bàn cuối lớp sẽ không thể nhìn thấy rõ được.
Chúng ta có hai phương pháp để giải quyết được vấn đề này, đó là:
2.1. Phương pháp thứ nhất
Thay đổi Font Size lớn hơn: Chọn Options => chọn Font Size => chọn 28pt
Phương pháp này có ưu điểm là rất nhanh chóng và áp dụng cho được cho tất cả các điểm hiện tại, nhưng nhược điểm của nó là toàn bộ cửa sổ GeoGebra cũng đều bị to lên theo.
2.2. Phương pháp thứ 2
Gán tên bằng công cụ Text và định dạng lại cỡ chữ, các bước thực hiện như sau:
+ Bước 1: Chọn công cụ Text
+ Bước 2: Nháy chuột trái vào điểm cần tạo tên => đánh dấu chọn vào LaTeX formula => nhập tên => và chọn OK
+ Bước 3: Nháy vào tên vừa tạo => chọn Graphics => chọn Large hoặc Very Large hoặc Extra Large
Phương pháp này tuy phải thực hiện qua nhiều bước, nhưng tên của điểm được tạo ra bằng phương pháp này khá đẹp mắt và có độ tùy biến khá cao.
Có một lưu ý dành cho các bạn khi sử dụng phương pháp này là trong trường hợp: tên ban đầu và tên tạo bằng công cụ Text là khác nhau – thì tên mà GeoGebra đọc sẽ được hiểu là tên ban đầu.
#3. Thiết lập ngôn ngữ tiếng Việt cho GeoGebra
GeoGebra không chỉ là một phần mềm đa nền tảng, mà nó còn là một phần mềm đa ngôn ngữ nữa. Cụ thể là phần mềm này hỗ trợ rất nhiều ngôn ngữ khác nhau trên thế giới, trong đó có tiếng Việt 🙂
Cá nhân mình thường không sử dụng tiếng Việt đối với các phần mềm nước ngoài. Bởi vì có khá nhiều từ khi được dịch sang tiếng Việt thì nghĩa của nó không còn được sát với nghĩa ban đầu.
Thậm chí trong một số trường hợp còn không thể dịch được mà phải mô tả. Cái thứ 2 nữa là khi gặp lỗi thì việc tìm kiếm cách sửa lỗi cũng khó khăn hơn, vì đa số các bài hướng dẫn đều là tiếng Anh.
Vì vậy cá nhân mình không khuyến khích các bạn sử dụng tiếng Việt với phần mềm GeoGebra. Mình chỉ sử dụng tiếng Việt khi phần mềm là thuần Việt mà thôi..
Trường hợp bạn muốn sử dụng giao diện tiếng Việt cho phần mềm GeoGebra thì bạn thực hiện theo các tao tác sau:
Chọn Options => chọn Language => chọn R – Z => chọn Tiếng Việt như hình bên dưới là được.
#4. Tùy chỉnh hộp thoại Preferences trong GeoGebra
Hộp thoại Preferences cho phép chúng ta tùy chỉnh các tùy chọn mà chúng ta yêu thích. Chẳng hạn bạn có thể tùy chỉnh màu sắc và kích thước của điểm, hoặc đoạn thẳng, hoặc…
Từ đây về sau mỗi khi tạo điểm, điểm sẽ tự động có màu sắc và kích thức như chúng ta đã tùy chỉnh trước đó.
Để mở hộp thoại Preferences bạn có thể vào Options => chọn Advanced …
4.1. Màu sắc và kích thước của điểm
Trong hộp thoại Advanced => bạn hãy chọn Defaults
GeoGebra hiện tại đang cung cấp cho chúng ta 5 loại điểm:
Free điểm tự do.
Dependent điểm phụ thuộc.
On Path điểm nằm trên đường.
In Region điểm nằm trong một miền nào đó (miền đa giác, hình tròn, …).
Complex Number điểm biểu diễn một số phức.
Chẳng hạn ở đây chúng ta cần tùy chỉnh lại màu sắc và kích thước của điểm tự do thì bạn hãy chọn Free => chọn
Color để tùy chỉnh màu sắc.
Style (Point Sixe) để tùy chỉnh kích thước của điềm tự do..
4.2. Màu sắc và kích thước của đoạn thẳng
Trong hộp thoại Advanced => bạn hãy chọn Defaults
Color để tùy chỉnh màu sắc.
Style (Line Thickness) để tùy chỉnh độ dày của đoạn thẳng.
Các đối tượng còn lại như DeLine, Ray, Polyline, Vector, Conic, Sector, … các bạn cũng tùy chỉnh tương tự nếu muốn nhé. Tùy thuộc vào đối tượng được chọn mà các tùy chọn trong hộp thoại Preferences sẽ có khác nhau đôi chút.
#5. Lời kết
Okay, trên đây là những bước tùy chỉnh lại GeoGebra trước khi sử dụng – giúp bạn làm việc hiệu quả hơn với phần mềm này.
Những tùy chỉnh bên trên là tương đối phù hợp với đại đa số người dùng Việt Nam chúng ta. Tất nhiên là bạn có thể tùy chỉnh khác theo sở thích cá nhân của bạn, miễn sao là nó phù hợp với bạn nhất là được.
Ngoài các tùy chỉnh cơ bản bên trên, GeoGebra còn cung cấp cho chúng ta rất nhiều tùy chỉnh khác, chẳng hạn như: đơn vị của góc, hiển thị/ không hiển thị (thanh nhập lệnh, thanh công cụ, thanh phụ, lưới, trục trung, trục hoành, …), kiểu lưới, màu lưới, …
Các bạn có thể tự tìm hiểu và khám phá thêm nhé. Xin chào tạm biệt và hẹn gặp lại các bạn trong những bài viết tiếp theo !
Bài viết được sự cho phép của tác giả Lê Xuân Quỳnh
Hello guys! Chúng ta code ios mỗi ngày, và chắc hẳn nhiều thanh niên giống mình ít khi check memory của app khi sử dụng để tìm hiểu những vấn đề bất thường của app. Trong ví dụ hôm nay chúng ta sẽ tự tay mình tạo ra leak để hiểu bản chất nó là gì, thay vì nghe các ông chém gió suông đọc xong rồi không hiểu gì cả.
Code ở branch bai1 nên bạn cần chuyển source về nhánh này nha. Cách chuyển xem phần mình pink sau:
Tuy nhiên tôi khuyên bạn chơi với terminal của MacBook cho nó pro nhé. Vì nếu bạn tải chay về bằng trình duyệt thì không thấy source code ở đâu đâu :v
Chơi với terminal đơn giản như sau:
Bạn gõ lệnh cd vào source code hôm trước bạn tải về: cd LeakMemorySample
2. Tiếp theo là gõ fetch để lấy source code mới nhất của tôi về: git fetch
3. Tiếp theo là bạn show toàn bộ branch trên repo của tôi bằng lệnh: git branch -a
Ở đây bạn sẽ thấy các branch sau: bai1 * bai2 master
Ví dụ bài này, tôi để hết source vào branch tên là bai2. Bạn chuyển sang source code bài 2 như sau: git checkout bai1
Sau khi branch bai1 được bôi đậm chuyển màu nghĩa là bạn đã thành công rồi đó. Còn nếu nó báo không thấy thì chứng tỏ bạn làm sai, hãy làm lại!
Tôi sẽ giải thích từng phần 1.
Màn hình thứ nhất: ViewController.
Tôi lười nên tôi lấy mặc định file do Xcode tạo luôn. Bạn vào file Main.storyboard để xem cách tôi layout kéo thả các thứ
Màn hình này chả có gì cả, ngoài cái ảnh logo web, 1 cái nút để mở sang màn thứ 2, nơi chứa leak memory.
2. Màn hình thứ 2: SecondViewController.
Màn này code như sau:
Tôi giải thích đoạn code trên:
Dòng 19 – tôi gọi hàm bindViewModel để kết nối viewmodel với view. Tôi thích làm MVVM cho code clean. Nên nếu bạn không hiểu về MVVM là cái gì, thì vui lòng tìm tag MVVM ở web này để hiểu thêm.
Từ dòng 23:
Tôi tạo viewModel ở đây. Ở dòng 27, tôi có 1 hàm closure. Hàm này làm cái gì? Nó chứa đoạn code 28(hoặc 29) set lại màu của background. Như ảnh thì tôi đang comment cái dòng 27 đằng sau, và dòng 28. Nếu bạn không biết gì về closure, thì hiểu nôm na nó là cái hàm chạy vào 1 thời điểm nào đó từ view model phát ra. Nó không chạy luôn ngay lúc khai báo đâu nha. Giống như bạn tạo 1 cái lịch biểu, rằng nếu như người yêu mà hôn bạn thì bạn làm cái gì đó Hôn ở đây là hàm needClosure, còn làm gì đó là dòng 28(hoặc 29).
Dòng 33, tôi tạo 1 hàm fake request API, có thời gian delay, và cuối cùng giả sử khi có kết quả thì nó sẽ call vào closure tôi nói ở trên.
Rồi vậy viewModel tôi có gì nào? Theo dõi code sau:
Ở dòng 11, tôi tạo 1 biến closure để bắn sự kiện ra khi API có kết quả trả về.
Dòng 12 tôi tạo 1 timer fake timeout của API.
Dòng 13 tôi tạo 1 biến lưu mảng số nguyên data.
Dòng 18 tới 21, tôi cố gắng tạo ra nhiều dữ liệu chiếm memory để cho bạn thấy sẽ tai hại thế nào khi ta không giải phóng nó. Tôi đẩy nó vào background thread để không block main thread khi bạn cố gắng mở SecondViewController.
Dòng 25 tới 30, tôi tạo fake 1 hàm call API. Hàm này sau 1 giây sẽ trả về kết quả. Khi có kết quả, nó sẽ gọi vào biến closure để bắn ra cho thằng SecondViewController xử lý. SecondViewController sẽ thực hiện việc đổi màu.
OK vậy là bạn đã hiểu luồng của app rồi. Vậy việc tiếp theo là theo dõi nó đã leak memory như nào.
Đầu tiên bạn chạy app và xem memory sử dụng như hình:
Bấm như hình để xem memory khi chạy app
Bạn bấm vào nút Open Second screen, sau đó đợi 1 lát cho SecondViewController tạo data rồi bấm back ra màn 1. Cứ làm như vậy dăm bảy lần, bạn sẽ thấy 1 điều là bộ nhớ chỉ có tăng mà không có giảm.
Chúc mừng bạn đã tạo leak memory thành công.
Thế tại sao nó lại leak memory?
Quay lại với đoạn code closure dưới đây:
Đoạn code gây leak memory
Ở đây tôi sử dụng liên kết mạnh từ viewModel sang view controller, bằng việc lấy self.view. Nghĩa là thằng viewModel này chỉ thực sự bị hủy khi thằng SecondViewController bị hủy. Tuy nhiên, thằng ViewModel này là con của thằng SecondViewController. Mà thằng SecondViewController muốn hủy thì nghĩa là con nó phải hủy trước. Ồ, vậy là bố bảo là con hủy thì bố mới hủy, con thì nói bố ơi bố hủy đi thì con mới hủy nè. Cuối cùng 2 thằng chả ai chịu ai, bộ nhớ vẫn cứ ở đấy, chả bao giờ giải phóng được.
Rất simple đúng không?
Vậy cách khắc phục thế nào? Chúng ta tiến hành sửa lại đoạn code trên như sau:
Tiến hành capture cho closure
Tôi tiến hành capture cho hàm closure trên. Ồ thế capture là gì? Đơn giản là thằng con nói bố ơi bố khi con có closure trả về, nếu như bố vẫn sống ở đó thì con sẽ set màu cho bố nhé! Mà bố ngỏm rồi(đã bị giải phóng khi bấm back) thì thôi con không gán nữa. Tôi tạo capture [weak self] để tạo liên kết yếu cho điều đó. Khi đó đoạn code ở closure sẽ là self?. thay vì self. Thật là đơn giản đúng không nào
Khi đó hàm sau sẽ in ra dòng chữ “free memory SecondViewController”
Từ nay về sau mỗi khi bạn tạo 1 view controller mới, bạn hãy thêm hàm deinit vào và đặt debug vào đó, xem nó đã được giải phóng chưa? Nếu như bạn đã hủy view controller rồi thì không có leak memory ở nó. Còn mà không in ra thì chắc chắn leak memory rồi bạn nhé.
Vậy là tôi đã trình bày xong trường hợp leak memory ở closure. Qua ví dụ hi vọng bạn sẽ cẩn thận hơn trong việc code của mình. App chạy được nhưng phải chạy mượt.
Phần mở rộng tôi viết gồm hubUrl, hubName là trỏ đến địa chỉ Hub cần kết nối tới. this.onOk là phương thức đảm bảo đang ở trạng thái kết nối mới thực hiện func (một hành động nào đó). Với classHub tôi đã xử lý hết các sự kiện khi mất kết nối, đang kết nối và kết nối
// Nếu bị mất kết nối thì 2s sau sẽ kết nối lại
$this.hub.connection.disconnected(function ()
{
console.log("disconnected");
state = 1;
var t = setInterval(function () { $this.doHubStart(true); clearInterval(t); }, $this.timeout);
});
$this.hub.connection.reconnecting(function ()
{
state = 1;
console.log("reconnecting");
});
// Đã được kết nối lại
$this.hub.connection.reconnected(function ()
{
console.log("reconnected");
var t = setInterval(function () { $this.doHubStart(true); clearInterval(t); }, $this.timeout);
});
Nếu bị mất kết nối, thì sau khoảng this.timeout (mặc định 2000ms) sẽ thực hiện kết nối lại.
Hub.ActionRegistry = function ()
{
this.registry = function (func) { /* Code code */ }
/* Code Code */
}
Với Hub.ActionRegistry được dùng để đăng ký các hành động sau khi nhận dữ liệu được push từ server gửi về. Tại sao tôi lại viết class này, vì tôi hướng tới có thể nhiều nơi, nhiều module mà cũng muốn nhận dữ liệu push về mà không phải lúc nào cũng phải sửa lại phương thức nhận dữ liệu tại client của Hub. Điều này cũng đồng nghĩa với việc lúc nào module nào dùng thì có thể đăng ký nhận dữ liệu.
Sau đây là code mẫu tôi viết cho chương trình chat đơn giản về sử dụng thư viện mở rộng trên
Tán gẫu nào
Trước tiên bạn cũng cài đặt SignalR và gắn thẻ script ở client
<scriptsrc='/signalr/hubs'></script> <!-- file js mà hub sinh ra --><scriptsrc='Hub.js'></script> <!-- Thư viện mở rộng của tôi -->
Code server để định nghĩa Hub
public class ExampleHub : Hub
{
public void ClientSendMessage(string userName, string msg, string color)
{
userName = userName.Trim();
if (userName.IsNull()) return;
msg = msg.Trim();
if (msg.IsNull()) return;
var data = new { userName, avatar = userName.First(), msg, time = DateTime.Now.ToString("HH:mm dd/MM/yyyy"), color };
Clients.AllExcept(Context.ConnectionId).receiveMessage(data, false);
Clients.Client(Context.ConnectionId).receiveMessage(data, true);
}
}
Code javascript tại client để kết nối tới Hub
function ExampleHub()
{
$.extend(this, new Hub("", "exampleHub"));
this.actionReceiveMessage = new Hub.ActionRegistry();
this.onBeforeStart = function ()
{
var $this = this;
// Client
this.hub.client.receiveMessage = function (data, right)
{
$this.actionReceiveMessage.do(function (func) { func(data, right); });
};
// Server
this.startDone = function () { };
this.clientSendMessage = function (userName, message, color)
{
this.onOk(function ()
{
$this.hub.server.clientSendMessage(userName, message, color);
});
}
};
}
Ở đây các bạn có thể thấy ExampleHub kế thừa tới Hub và gán 2 giá trị: hubUrl = “” (Do server của hub nằm ngay tại server web của tôi) và hubName = “exampleHub”.
Ở phía xử lý client có this.actionReceiveMessage là đối tượng đăng ký nhận hành động muốn xử lý data gửi về từ server. Client muốn giao tiếp gửi request tới server thì thông qua this.onOk để luôn đảm bảo chỉ thực hiện request khi ở trạng thái kết nối ổn định.
Và cuối cùng là giao diện hiển thị tôi định nghĩa một BoxChat như sau
function BoxChat()
{
this.ws = null; // là một instance của ExampleHub
this.area = null;
var template = null;
var colors = { 0: "bg-danger text-white", 1: "bg-success text-white", 2: "bg-primary text-white", 3: "bg-warning text-white", 4: "bg-secondary text-white", 5: "bg-dark text-white", 6: "bg-orangered text-white" };
var names = { 0: "Sơn20", 1: "Dũng", 2: "Việt Anh", 3: "TuhiKing", 4: "Tàn Long", 5: "ButaKing", 6: "Osoft" };
var color = null;
this.start = function ()
{
color = colors[Core.random(6)];
var $this = this;
template = this.area.find("[data-form=template] .direct-chat-msg");
var userNameInput = this.area.find("[name=userName]");
var messageInput = this.area.find("[name=message]").focus();
var butonSend = this.area.find("[data-form=button]");
userNameInput.val(names[Core.random(6)] + "." + Core.random(20)); // Tạo một tên ngẫu nhiên
butonSend.click(function ()
{
var userName = userNameInput.val();
if (userName == "") { Core.alert("Vui lòng nhập tên tài khoản để tán gẫu"); return; };
var message = messageInput.val();
if (message == "") { Core.alert("Vui lòng nhập nội dung để tán gẫu"); return; };
$this.ws.clientSendMessage(userName, message, color);
});
messageInput.keypress(function (event)
{
if (event.keyCode == "13")
{
butonSend.click();
return false;
}
});
var formChat = this.area.find("[data-form=chat]");
this.ws.actionReceiveMessage.registry(function (data, right)
{
var chatItem = template.clone();
chatItem.find("[data-chat]").each(function () { $(this).html(data[$(this).attr("data-chat")]); });
chatItem.find("[data-chat=avatar]").addClass(data.color);
if (right) chatItem.addClass("right");
formChat.append(chatItem);
formChat.scrollTop(formChat[0].scrollHeight);
messageInput.val("").focus();
});
}
}
Tại hàm start của BoxChat tôi đăng ký hành động khi có message gửi về thì hiển thị nội dung tin nhắn lên. Như đã nói ở trên, actionRegistry giúp đăng ký hành động nhận push từ server ở mọi module mà mình muốn mà không cần phải sửa lại hàm ở client trong hub.
Cuối cùng là khai báo và sử dụng
var ws = new ExampleHub();
ws.timeout = 2000; // Mặc định
ws.start();
var boxChat = new BoxChat();
boxChat.ws = ws;
boxChat.area = $("article");
boxChat.start();
// Ví dụ viết code đăng ký thêm một hành động nhận dữ liệu từ server push về
// Thì hiển thị một notify bên góc phải trên cùng màn hình
// Để cho thấy là có thể đăng ký sự kiện nhận dữ liệu ở mọi nơi.
ws.actionReceiveMessage.registry(function (data, right)
{
new PNotify({ target: $("article"), text: data.userName + ":" + data.msg, type: "success", delay: 1000 });
});
Tại bài viết này tôi vẫn đang sử dụng asp.net SignalR mà chưa chuyển sang asp.net Core SignalR. Do chưa đủ điều kiện về vật chất, hạ tầng và nhất là hệ thống phần mềm hiện tại của tôi vẫn đang viết trên asp.net. Tuy nhiên với cách làm này của tôi thì chuyển sang asp.net Core tôi cũng sẽ làm tương tự. Và đây là chia sẻ nhỏ mà tôi đã làm trong thực tế.
Gửi cảnh báo khi nhân viên thêm phiếu chi với một mục đích chi nào đó bị quá giới hạn cho phép mà quản lý đã cấu hình
Click vào cảnh báo ở góc phải bên dưới màn hình xem ngay được nội dung cảnh báo
Tại nội dung cảnh báo. Click xem chi tiết phiếu để xem nhân viên đã thêm chi cho những mục đích gì
Chúc các bạn lập trình ngày càng tốt hơn. Nếu có thắc mắc các bạn hãy để lại bình luận nhé. Xin cảm ơn
Bài viết được sự cho phép của tác giả Kien Dang Chung
Các lập trình viên gạo cội chắc không lạ gì với JasperReport, nhưng với một số người mới JasperReport có thể là một khái niệm rất mới. Vậy JasperReport là gì? nó được ứng dụng như thế nào trong các hệ thống phần mềm?
Trong các hệ thống ứng dụng, các mẫu biểu chứng từ, các loại tài liệu báo cáo… là rất cần thiết. Ví dụ, với một hệ thống quản lý bán hàng chúng ta có các mẫu chứng từ như phiếu xuất kho, phiếu bảo hành… Hệ thống ngân hàng có thể có các chứng từ như các mẫu thu chi, phiếu tạo khách hàng, phiếu gửi tiền… JasperReport là bộ máy tạo các báo cáo mã nguồn mở được viết bằng ngôn ngữ Java giúp tích hợp vào các hệ thống nhằm tạo ra các mẫu biểu báo cáo, chứng từ phức tạp một cách dễ dàng.
JasperReport là một thư viện viết bằng Java, vậy tại sao Allaravel một website chuyên về framework Laravel lại giới thiệu JasperReport? Bạn có thể tham khảo bài viết Xây dựng báo cáo với JasperReport trong Laravel để hiểu hơn. Quay lại với bài viết, giờ thì bạn có thể đã hiểu sơ bộ được JasperReport là gì?
JasperReport có rất nhiều ưu điểm để trở thành một lựa chọn tốt nhất của bạn cho việc xây dựng hệ thống xuất chứng từ:
JasperReport là phần mềm mã nguồn mở được phát triển từ năm 2001, do vậy nó hoàn toàn miễn phí và có một cộng đồng sử dụng rất lớn.
Có rất nhiều các công cụ được phát triển giúp cho việc xuất báo cáo với JasperReport đơn giản:
iReport là công cụ giao diện trực quan GUI giúp tạo ra các mẫu chứng từ chính xác dễ dàng. Phiên bản cuối cùng 5.5.0 phát hành ngày 31/12/2015 và sau đó chuyển sang công cụ mới là JasperSoft Studio.
JasperSoft Studio công cụ thế hệ mới của iReport. Trong các bài viết của loạt bài về JasperReport chúng ta sẽ chỉ sử dụng JasperSoft Studio thay cho iReport.
JasperStarter là công cụ giúp xuất báo cáo thông qua các câu lệnh trong màn hình dòng lệnh (console).
JasViewer công cụ giúp chạy các file jasper, hiển thị người dùng tham số đầu vào chứng từ, hiển thị nội dung chứng từ.
JasperReport Server là máy chủ chạy báo cáo có thể tích hợp cho phép chạy các báo cáo thời gian thực hoặc theo lịch trình dựa trên nền tảng web. Khi xây dựng các file báo cáo bằng iReport hoặc JasperSoft Studio, các file này có thể triển khai lên máy chủ và được thực hiện, xuất ra các định dạng khác nhau.
2. So sánh JasperReport với các hệ thống báo cáo khác như Crystal Report, KoolReport
Crystal Report là công cụ báo cáo rất quen thuộc với các lập trình viên .NET do nó tích hợp sâu vào Visual Studio .NET. Microsoft sau đó cũng phát triển riêng một hệ thống báo cáo là Microsoft Report. Sự khác biệt lớn là Crystal Report là phần mềm có phí do vậy để sử dụng bạn phải mua bản quyền.
KoolReport là hệ thống báo cáo viết bằng ngôn ngữ PHP, nó cũng là lựa chọn tốt khi sử dụng framework Laravel, đặc biệt nó có một gói tích hợp giúp phát triển báo cáo KoolReport trong Laravel nhanh chóng mà không cần phải viết nhiều code. Tuy nhiên, để thiết kế được báo cáo chúng ta cần có KoolReport Pro với giá 129$.
Qua rất nhiều so sánh chúng ta có thể thấy JasperReport là lựa chọn tốt với chi phí thấp (gần như không mất phí) cho phép tạo ra các chứng từ phức tạp nhất với các thành phần báo cáo hiện đại như biểu đồ, các loại mã vạch, QR code, bản đồ…
3. Cấu trúc thư viện JasperReport
net.sf.jasperreports.engine.JasperCompileManager: sử dụng để biên dịch JRXML report template.
net.sf.jasperreports.engine.JasperFillManager: Sử dụng để fill report với dữ liệu từ data source.
net.sf.jasperreports.engine.JasperPrintManager: Sử dụng để in tài liệu được sinh ra từ các thư viện JasperReports.
net.sf.jasperreports.engine.JasperExportManager: Sử dụng để xuất ra các định dạng PDF, HTML, hoặc XML.
net.sf.jasperreports.view.JasperViewer: Hiển thị một ứng dụng Java Swing đơn giản có thể hiển thị các báo cáo.
net.sf.jasperreports.view.JasperDesignViewer: Sử dụng để thiết kế và xem trước report templates.
4. Lời kết
JasperReport với bề dầy về phát triển gần 20 năm và một cộng đồng lớn kế thừa từ Java, Eclipse… đã tạo nên một hệ sinh thái xoay quanh các báo cáo, các hệ thống phân tích ra quyết định BI cực phong phú với giá thành cực thấp. JasperReport là lựa chọn tốt khi bạn muốn triển khai các hệ thống báo cáo, hệ thống in ấn chứng từ… tích hợp với các ứng dụng trên nền tảng Laravel. Chúng tôi mong muốn được đem đến cho các bạn các kiến thức cơ bản nhất giúp ích cho công việc các bạn sau này, các kiến thức có thể chưa được biên tập bài bản và đôi khi đưa ra theo những suy nghỉ cảm tính rất mong nhận được sự góp ý từ các bạn bè.
Trước khi bắt đầu tìm hiểu Mybatis separator là gì?. Nếu yếu sinh lí, ý lộn, nếu chưa biết về Mybatis thì các ông có thể đọc bài giới thiệu về Mybatis ở Kieblog.
Trong quá trình làm việc tại công ty, gặp phải một dự án sử dụng Mybatis. Va vấp khá nhiều, rút ra không ít kinh nghiệm khi làm việc với chim nhỏ, nên viết ra bài này chia sẻ chút kinh nghiệm nhỏ nhoi khi làm việc với Mybatis separator.
Mong anh em đón đọc, có gì chưa đúng xin vui lòng bình luận phía dưới. Đm, tôi thề là ông nào chửi tôi cũng rep lại lịch sự nhé!.
Nhìn quen không?. Dynamic với Mybatis rõ ràng render động giấu phẩy giữa các điều kiện IN
Trong trường hợp này seperator giúp thêm các dấu phẩy giữa các điều kiện IN. SQL sẽ generate ra như sau:
SELECT * FROM COURSES
WHERE tutor_id IN (1,2,3)
Không phải chỉ mỗi cho các điều kiện WHERE IN, seperator còn hỗ trợ loop đối với các câu SQL cơ bản. các cặp value phức tạp hơn
SELECT * FROM COURSES
WHERE (tutor_id, lession_id) IN ((1, 2), (2,3), (3,4))
2. Kinh nghiệm xương máu
Sử dụng Mybatis separator thật sự tiện lợi. Nói thẳng ra nhắc tới Dynamic SQL mà không có ví dụ về Mybatis separator thì cũng hết cả vui, hết cả sinh động.
Tuy nhiên, thực tế cho thấy, cần phải thật sự cẩn thận khi sử dụng separator.
Chuyện thế này, ở công ty, khi cả team tôi đang phát hục mặt làm task cho kịp tiến độ thì QC hét lên.
Ủa, sao cái bảng ABC này có tới cả triệu dòng thế?. Ai đã làm gì?. Mới chạy test lòng vòng có vài lần. Sao dữ liệu đâu ra lắm thế?.
Chả có gì để nói nếu tôi không phải là người vô tình tìm ra cái bug do vô tình mà có này.
Nói là bug vô tình nhưng nếu cứ để vậy mà release thì bug này không phải chuyện nhỏ. Tất cả các câu SQL viết cho dự án đều được yêu cầu tối ưu triệt để. (Anh em nào chưa biết thì đọc qua bài viết này Tối ưu SQL, jouneu to the west)
Dự án thì đang dùng Amazon EC3, nếu mới trong một thời gian ngắn sử dụng mà đã có tới mấy chục triệu row trong table thì thật là lạ (dự án đâu làm về big data)
Cắm đầu nhìn code, ban đầu thì chả có gì đặc biệt, anh đồng nghiệp insert một list data. Tất nhiên ảnh sẽ dùng foreach của separator, truyền vào một list, rồi cứ loop trên list đó để insert vào DB thôi.
Đại khái viết thế này:
// Tôi viết đại khái thôi nhé!.
// Chứ theo policy công ty không thể public source được.
<foreach collection="danh_sach_group" item="group" separator="UNION">
<foreach collection="group" item="model">
insert into BANG_NAO_DO
(ten, tuoi)
values
(#{model.ten}, #{model.tuoi})
</foreach>
</foreach>
3. Vấn đề ở đâu?
Như cái ví dụ phía trên tôi post lên thì các ông thấy vấn đề nằm ở đâu không?.
Vấn đề nằm ở chỗ cái separator=”UNION” đấy, thật sự tai hại. Nói không chắc một số anh em chưa hiểu được (cái Dynamic SQL thật sự cần trí tưởng tượng một chút).
Tôi lấy ví dụ nhé:
// Công ty có 2 group (A và B)
Group A - 2 thằng (Tên TÈO - 11 tuổi, tên TỒ - 12 tuổi)
Group B - 3 thằng (Tên TÍ - 13 tuổi, tên TỦN - 14 tuổi, tên TỊT - 15 tuổi)
// Với 2 group này, ta cần 5 câu insert vào BANG_NAO_DO
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TEO, 11)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TO, 12)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TI, 13)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TUN, 14)
INSERT INTO BANG_NAO_DO (ten, tuoi) VALUES (TIT, 15)
Đấy, yêu cầu dùng Mybatis separator chỉ có thế. Insert 5 thằng trong 2 group vào là ổn, nhưng đây lại insert tới gấp đôi, gấp ba. Bảo sao database không quá tải!.
Ra production kiểu này là toang, dễ đi lắm, dễ ăn chửi lắm!. Nên anh em nhớ cẩn thận nha
4. Kết luận
Dynamic SQL thật sự rất tiện lợi, từ khóa Separator cũng vậy. Trường hợp sử dụng lịnh hoạt, SQL sẽ làm rất nhiều việc, bớt phải code ở phần logic Java.
Tuy nhiên, trường hợp sử dụng cần đặc biệt chú ý các từ khóa foreach, separator by UNION, OR, AND. Trước khi viết cần lường được trước câu SQL sẽ render như thế nào?.
Nhớ lường trước cái nguy hiểm của seperator UNION trước khi bắt đầu nha!
Nếu không thể lường trước, phải cố gắng show log câu SQL được render ra như thế nào rồi run thử. Tránh các trường hợp SQLException khi chạy production thực tế.
Bài viết được sự cho phép của tác giả Nguyễn Văn Trọng
Làm việc với vai trò của BrSE là người trung gian thực sự không phải việc đơn giản, vì 1 quyết định đưa ra sẽ có ảnh hưởng đến cả nhiều phía. Dưới đây là 1 số tình huống dở khóc dở cười mà mình đã từng gặp phải.
Khi phát hiện ra hệ thống cần có thêm chức năng abc, nếu nói cho khách hàng biết thì offshore sẽ nổi khùng vì phải overtime, mà không nói thì khách hàng cũng không biết, vậy nên nói không ?
Khi khách hàng không chấp nhận estimate vì các bác nghĩ quá cao, còn nếu giảm xuống thì bên phía nhà mình cho rằng quá thấp, thì phải làm thế nào ?
Khi khách hàng yêu cầu điều tra 1 lỗi hệ thống mà phần này mình chưa từng làm bao giờ, từ chối thì sẽ bị đánh giá thấp, mà nhận nếu làm không được thì gay go nữa ? vậy phải làm sao ?
Để trở thành 1 BrSE tốt thì trong quá trình làm việc hay mỗi lần đưa ra quyết định phải dựa trên nhiều nguyên tắc. Quyết định thì có thể đúng hoặc sai, nhưng 1 khi đã làm thì hãy dứt khoát, có sai thì mới có đúng – có dở thì sau này mới hay được.Có 3 nguyên tắc chính mà bản thân mình cho là quan trọng nhất.
Mình sẽ đưa dẫn chứng cụ thể để giải thích, mời các bạn cùng tham khảo nhé.
Tại sao lại vậy ? đơn giản “khách hàng là thượng đế”, còn nếu bạn muốn làm thượng đế thì … cái này mình bó tay thật.
Các cụ có câu (các cậu có …) “Thương nhau cau sáu bổ ba, ghét nhau cau sáu bổ ra làm mười” – cái ni là mạ miềng (mẹ mình) dạy. Khi mà các bác khách hàng thương mình rồi mọi việc sau này nó dễ dàng vô cùng, làm đúng thì bị khen lấy khen để, mà sai nhỏ nhỏ thì cũng … xí xóa cho qua. Để các bác ấy thương thì phải chiều, chiều sao cho đúng ? thì đơn giản là nhìn mọi việc trên view khách hàng sẽ hiểu ra được những mong muốn sâu xa mà các bác ko nói, hoặc đôi khi chưa nghĩ ra để nói.
Dạo trước, có lần mình phát hiện ra 1 lỗi khá to trong FW của khách hàng (FW này phát triển dựa trên Struts và Spring), lúc này cực kỳ khó xử vì nếu sửa 1 phát thì sẽ phải test lại toàn bộ, anh em offshore Over Time vỡ mật luôn. Mình quyết định nói nhưng kèm theo điều kiện là xin cho mọi người nghỉ vài ngày xả hơi sau khi xong cái ấy. Kết quả là giai đoạn đó tuy căng thẳng nhưng đổi lại từ đó về sau FW chạy rất mượt mà, khách hàng hài lòng còn anh em ở nhà cũng thoải mái hơn rất nhiều.
Đứng về phía khách hàng nhưng cũng phải nghĩ cho bên mình sao cho hài hòa
Nguyên Tắc 2 : Khách Hàng Không Phải Lúc Nào Cũng Đúng
Hãy luôn đặt nghi vấn
Đối với người Nhật, họ luôn chỉn chu trong mọi quyết định, thường sẽ họp hành bàn bạc rất kỹ lưỡng, làm gì cũng có lý do hết. Nên nói họ sai thì rất hiếm khi, nhưng có hoàn toàn hợp lý chưa thì chưa chắc. Hãy luôn đặt ra nghi vấn với yêu cầu. Tại sao họ là yêu cầu làm vậy ? có cách nào hay hơn không ?
Cách đây 3 năm, trong 1 dự án mà các bác giao cho team BrSE mình làm từ design (basic + detail) đến test nghiệm thu. Thì ở giai đoạn design các bác muốn tụi mình làm = WORD. Thiệt sự mình cực kỳ ghét làm với word mà chỉ thích exel, 2 anh trong team cũng vậy vì nó rất khó format, bể tùm lum, ko thích hợp để làm design với đặc thù dự án lúc đó. Rất may là dự án trước đó mình cũng từng dùng word để làm rồi nên hiểu được những khó khăn khi phải chỉnh sửa nhiều, khi mình kể chuyện này cho các bác ấy nghe thì được chấp nhận chuyển đổi yêu cầu. Và sau này viết thêm mấy cái tool macro (word thì bótay) Gen tài liệu nữa nên năng suất vù vù mới thấy quyết định đó quá đúng đắn.
Nguyên Tắc 3 : Không Nhất Thiết Phải Nói Thật Nhưng Tuyệt Đối KHÔNG ĐƯỢC NÓI DỐI
Luôn tôn trọng sự thật
Đây là kinh nghiệm mình được 1 anh senpai truyền lại, trải nghiệm và thấy nó rất cần thiết. Người Nhật ghét nhất 2 thứ : ăn cắp và dối trá. Dân tộc Nhật, nền kinh tết Nhật dựa trên niềm tin, làm giàu bằng niềm tin từ khách hàng nên dối trá đối với họ là cái gì đó rất kinh khủng.
Khi khách hàng hỏi “tiến độ coding đến đâu rồi mậy”, còn 1 tuần nữa release mà đang cháy bùng bùng, cái module to nhất thì chú dev phụ trách lại nghỉ phép giữa chừng vì cô người yêu bị … sổ mũi. Tình hình có vẻ căng ! trả lời sao đây ?
“dạ bác ok lắm ah” – vừa nói xong bỗng nhận được mail xin dời deadline của PM bên offshore => vỡ mặt.
“Dạ Function xxxOzawa do chú Kim phụ trách nhưng chú ấy xin nghỉ phép rồi ah, chắc là sẽ bị trễ … vài ngày” => vỡ đầu.
Lúc này tốt nhất là xin trả lời sau, trước mắt phải bàn bạc cụ thể với PM để bàn đối sách giải quyết, đưa ra giả thuyết cho trường hợp tốt nhất – xấu nhất, rồi sau đó mới thành thật với khách hàng (thành thật về tiến độ – kèm đối sách, nhưng mấy cái lý do lãng xẹt liên quan chú dev tên Kim thì ko nên nói ra).
Tổng Kết
Đánh giá sự việc trên view khách hàng
Khách hàng không phải lúc nào cũng đúng.
Không nói dối nhưng không nhất thiết phải nói thật.
Trên đây là bộ 3 nguyên tắc cơ bản của bản thân mình, tất nhiên là cũng có vài nguyên tắc khác nhưng mình thấy nó không quan trọng lắm nên ko liệt kê. Các bạn có thể áp dụng theo nhưng đừng cứng nhắc quá nhé, nếu có gì hay cứ share cho mình học hỏi thêm ờ comment bên dưới nhé.
Bấm tổ hợp phím Ctrl+Shift+A (Windows) hoặc Cmd + Shift + A (MacOS), tìm Registry. Khi hộp thoại Registry hiển thị thì kích hoạt (enable) cấu hình compiler.automake.allow.when.app.running
Bước 3 — Khởi động lại IntelliJ và thưởng thức
Chú ý:
Một số tình huống có thể xảy ra khi sử dụng tính năng Hot Reload là lỗi chạy lại nếu có sử dụng@Autowired trong mã nguồn Spring. Cách giải quyết: dùng phương pháp inject qua contructor thay vì @Autowired .
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Java cho phép chúng ta có thể định nghĩa một class nằm trong một class khác, ví dụ như:
package com.huongdanjava.javaexample;
public class Application {
class Builder {
}
}
Class được định nghĩa bên trong một class khác, chúng ta gọi nó là nested class còn class khác này được gọi là outer class. Trong ví dụ của mình ở trên thì Builder là một nested class còn Application là một outer class. Nested class còn được chia thành 2 loại khác nhau là Non-static nested class và Static nested class. Cụ thể như thế nào? Trong bài viết này, chúng ta sẽ tìm hiểu về các nested class trong Java các bạn nhé!
Non-static nested class hay còn gọi là những Inner class. Chúng ta có thể declare những Inner class này bên trong một class khác như class Builder ở trên, bên trong một phương thức nào đó (Method-local Inner Class), ngoài ra chúng ta còn có những class không có tên gọi là Anonymous Inner Class.
Khác với việc khai báo một class, chúng ta không thể declare nó với private access modifier, đối với Inner class chúng ta có thể làm điều này. Như ví dụ trên, mình có thể declare class Builder là private như sau:
package com.huongdanjava.javaexample;
public class Application {
private class Builder {
}
}
Mà khi đã declare một Inner class là private thì nó chỉ có thể được access bên trong class nó được khai báo thôi các bạn nhé!
Để khởi tạo một Inner class, các bạn cần khởi tạo đối tượng cho outer class trước. Ví dụ như:
package com.huongdanjava.javaexample;
public class Application {
private class Builder {
}
public static void main(String[] args) {
Application app = new Application();
Builder builder = app.new Builder();
}
}
Khi chúng ta khai báo một class bên trong một method nào đó, gọi là Method-local Inner Class, thì scope của class này chỉ thuộc về method đó mà thôi. Các bạn không thể khai báo class này với access modifier trong trường hợp này. Ví dụ như:
package com.huongdanjava.javaexample;
public class Application {
public void print() {
class Builder {
}
}
}
Và do đó, chúng ta chỉ có thể khởi tạo đối tượng của class này bên trong method:
package com.huongdanjava.javaexample;
public class Application {
public void print() {
class Builder {
}
Builder builder = new Builder();
}
}
Các bạn có thể khai báo final class hoặc abstract class bên trong một method các bạn nhé:
package com.huongdanjava.javaexample;
public class Application {
private void print() {
abstract class Builder {
}
}
}
Hay:
package com.huongdanjava.javaexample;
public class Application {
private void print() {
final class Builder {
}
}
}
Anonymous Inner Class thường được khai báo khi chúng ta muốn override một phương thức nào đó của một class hoặc interface. Chúng ta sẽ khai báo Anonymous Inner Class và khởi tạo đối tượng cho class này cùng một thời điểm. Ví dụ như:
package com.huongdanjava.javaexample;
public class Application {
public void print() {
Comparable<String> comparable = new Comparable<String>() {
public int compareTo(String o) {
return 0;
}
};
}
}
Các bạn cũng có thể declare Anonymous Inner Class bên ngoài method như sau:
package com.huongdanjava.javaexample;
public class Application {
Comparable<String> comparable = new Comparable<String>() {
public int compareTo(String o) {
return 0;
}
};
public void print() {
}
}
Static nested class là những class được định nghĩa với từ khoá static. Nó là static member của outer class và do đó chúng ta không cần phải khởi tạo đối tượng của outer class và chính class này để access tới nó luôn. Ví dụ như:
package com.huongdanjava.javaexample;
public class Application {
static class Builder {
private static void print() {
}
}
public static void main(String[] args) {
Application.Builder.print();
}
}
Static nested class sẽ không access được tới các field, methods non-static của outer class. Ví dụ như:
Chúng ta không thể declare Static nested class bên trong method các bạn nhé!
Bữa rồi rảnh rỗi ngồi lướt Facebook thấy có status đăng như sau “Phỏng vấn Vuejs một bạn làm Vuejs 2 năm nhưng không rõ về mounted, computed và created“.
Nghĩ mà buồn thay, nên tiếp sau bài viết về Vuejs life cycle – hiểu sao cho đúng. Mình quyết định viết thêm bài viết một số câu hỏi phỏng vấn Vuejs cơ bản. Hy vọng sẽ giúp đỡ các bạn khi try hard phỏng vấn Vuejs nha.
v-if only renders the element to the DOM if the expression passes whereas v-show renders all elements to the DOM and then uses the CSS display property to show/hide elements based on expression.
v-if chỉ render các element tới cây DOM nếu biểu thức bên trong dấu bằng đúng (true). Trong khi đó, v-show render tất cả các element đó trên DOM và sử dụng CSS để ẩn hiện thông qua tính đúng sai trong biểu thức
Rõ ràng mà nói, sự khác biệt cơ bản và chuẩn chỉnh nhất giữa v-if và v-show là có render lên DOM tree hay không?. V-if rõ ràng không hề render object đó nếu không thỏa điều kiện, còn v-show thì có.
v-if has higher toggle costs while v-show has higher initial render costs
v-if có chi phí render khá cao, chắc chắn là cao hơn v-show, thoải điều kiện thì v-show sẽ render lại object đó trên DOM. Nên cẩn thận khi dùng
// v-show render trên DOM, nhưng display: none để ẩn đi
<h1 v-show="ok">Hello!</h1>
Nên một số đối tượng thường xuyên ẩn hiện nên sử dụng v-show. Bạn nào chưa biết về cây DOM có thể tham khảo bài viết này tại Kieblog.
Đấy, đọc thì tưởng là câu hỏi phỏng vấn Vuejs dễ. Trả lời cho đúng với bài bản thì không phải dễ nha.
2. Tại sao khi bind v-for thường phải mapping key
Hơi kì kì nhưng câu trả lời cũng có ý đúng là v-if thì có v-else còn v-show thì không
3. Có thể for loop một range với v-for được không?
Căng, trước giờ v-for với index cho từng item thì thấy nhiều, chứ for cho range thì hơi lạ. Tuy nhiên đây không phải câu phỏng vấn Vuejs khó. Chỉ là một câu hỏi mẹo thôi
v-for với kiểu mapping idx kiểu này thì bình thường, xài rất nhiều.
<div
class="message-content"
v-for="(msg, idx) in message.contents"
v-bind:key="idx"
v-html="msg"
></div>
Tuy nhiên, vẫn có một kiểu khác ít biết hơn có thể for trong range như sau:
// Sử dụng từ khóa in, loop trong dãy từ 1->10
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
Đù, hỏi thách đố vl, nhưng không sao, biết thêm chừng nào tốt chừng đó. Code kiểu này trong pro ra hẳn.
4. Dynamic route matching là gì?
Dynamic route cũng là một câu hỏi phỏng vấn khá hay. Tất nhiên đã làm nhiều với Vuejs sẽ trả lời hoặc đưa ra được ví dụ về câu hỏi này. Đã làm Vue thì mình nghĩ trong quá trình phỏng vấn Vuejs chắc chắn sẽ có một câu liên quan tới Router. Nên ôn kĩ nha!.
Tuy nhiên, chính xác mà nói:
Dynamic route matching to map routes to the same component based on a pattern.
Dynamic route matching giúp ta map các route tới component với các pattern khác nhau
Cùng xem xét ví dụ dưới đây:
// Sử dụng từ khóa in, loop trong dãy từ 1->10
const Aritcle = {
template: '<div>Aritcle {{ $route.params.subjectId }}, PostId: {{ route.params.postid }}</div>'
}
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/subject/:subjectId/post/:postid', component: Aritcle }
]
})
Khi sử dụng, các URL mapping với router param sẽ cho các kết quả như sau:
/subject/vuejs/post/123 hoặc /subject/react/post/234
5. Mục đích của keep alive tag?
Câu hỏi phỏng vấn Vuejs về keep-alive thường không hỏi nhiều. Tuy nhiên khá dễ để nắm bắt, bạn nào có đọc qua chắc chắn trả lời được.
Keep-alive tag is an abstract component used to preserve component state or avoid re-rendering. When you wrapped tag around a dynamic component, it caches the inactive component instances without destroying them.
Keep-alive tag sử dụng để giữ trạng thái hiện có của component và tránh việc render lại component quá nhiều lần. Các đối tượng được đóng ở trong tag keep-alive sẽ được giữ instances
Keep-alive component đặc biệt hữu ích ở các component stepper. Một khi đã lưu các thông tin ở step này, back lại sẽ có ngay, không phải render.
<!-- Inactive components will be cached! -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
Ngoài việc không phải render lại (về perfomance), sử dụng keep alive tag còn tránh việc sử dụng Store vô tội vạ để lưu trữ khi back đi back lại
<!-- basic -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- multiple conditional children -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
<!-- used together with `<transition>` -->
<transition>
<keep-alive>
<component :is="view"></component>
</keep-alive>
</transition>
Trong bài viết này, Smartjob sẽ chia sẻ cho các bạn một câu chuyên khá hi hữu về chàng trai hiện đang là lập trình viên của gã khổng lồ Google khi anh ấy bằng một cách thần kỳ đã bảo toàn được tới 90% lương của mình. Quả là một câu chuyện đáng học hỏi cho những ai muốn tiết kiệm thật nhiều cho tương lai của mình.
Chàng trai đó tên là Brandon, 24 tuổi, trở thành kỹ sư phần mềm của Google từ tháng 5/2015. Anh ấy đã có ý tưởng sống trong một chiếc xe tải để thắt chặt chi tiêu của mình khi bắt đầu thực tập tại Google giữa năm 2014. Vào thời điểm đó, Brandon cùng 3 người bạn của anh đã thuê một căn hộ 2 giường ngủ với mức giá rẻ nhất San Francisco (khoảng 65USD/đêm tương đương 2000 USD/tháng).
Chiếc xe của Brandon tại khuôn viên Google
Sau kì thực tập, Brandon đã thuyết phục hoàn toàn được các sếp trong Google và trở thành kỹ sư phần mềm tại đây. Ngay sau đó, anh đã chi khoảng 10.000USD để mua chiếc xe tải cũ sản xuất năm 2006, nhãn hiệu Ford cỡ nhỏ. Chiếc xe này đã chạy được hơn 252.000km nhưng vẫn còn rất tốt đối với Brandon. Thực chất số tiền 10.000USD là số tiền thưởng mà Google đã dành cho Brandon khi anh ký hợp đồng với hãng này.
Chiếc xe tải của Brandon rộng gần 12m2, không quá lớn nhưng đủ để anh đặt 1 chiếc giường, 1 tủ quần áo, 1 giá treo đồ và 1 lồng thú cưng :D. Theo lời anh, trước đó dù được ở trong một căn hộ nhưng anh hoàn toàn không thoải mái và không có cảm giác như ở nhà. Với Brandon đó chẳng khác gì ném tiền qua cửa sổ. Brandon chia sẻ thêm: “Thật khó để biện mình cho hành động ném tiền qua cửa sổ như thế. Về cơ bản, tôi đang lãng phí tiền bạc. Số tiền ấy chẳng để góp vốn cho cái gì hay xây dựng tương lai vì thế tôi không đành lòng tiếp tục thuê căn hộ.”
Tại San Francisco, số tiền mà chàng trai này phải bỏ ra cho việc mua bảo hiểm xe tải là 121USD/tháng. Bên cạnh đó, hóa đơn tiền điện thoại anh dùng sẽ do Google chi trả. Để tiết kiệm hơn, anh cũng ăn 3 bữa và tắm giặt ngay tại văn phòng. Và đặc biệt, anh không mua thêm bất cứ vật dụng nào liên quan đến điện. Anh chia sẻ: “Tôi không sở hữu bất cứ vật gì dùng đến điện. Chiếc xe tải của tôi có sẵn đèn. Tôi có một chiếc đèn pin để sử dụng vào ban đêm. Ngoài ra, tôi chỉ có một cục pin nhỏ để dự phòng, vài ngày mới sạc một lần trên công ty. Tôi dùng cục pin này để sạc tai nghe và điện thoại di động. Còn laptop của tôi có thể chạy qua đêm sau mỗi lần sạc đầy tại văn phòng.”
Brandon đặt ra mục tiêu là tiết kiệm 90% thu nhập sau khi đã trừ các khoản thuế. Anh sẽ dùng số tiền đó để trả nợ đã vay từ thời còn sinh viên và đầu tư cho các kế hoạch của riêng mình. Anh cũng tiết lộ rằng anh đã vay số tiền 22.434USD để trang trải cho việc học đại học và với việc tiết kiệm triệt để trong vòng 4 tháng, anh đã trả được 16.449USD và theo như các tính toàn của anh, tất cả số tiền anh đã vay sẽ được hoàn trả trong vòng 6 tháng tới. Trong 10 đến 20 năm nữa, anh sẽ có trong tay hàng trăm ngàn USD với chính sách thắt chặt chi tiêu như hiện nay.
Chia sẻ thêm về cuộc sống của Brandon: anh luôn là người có mặt sớm nhất công ty bởi mỗi sáng sớm anh chỉ tốn vài bước chân đi bộ từ oto đến nơi làm việc trong khi đồng nghiệp của anh phải chờ hàng giờ trên tàu điện hoặc chết đứng trong đám kẹt xe. Anh nghĩ anh vẫn còn trẻ và lựa chọn cuộc sống như vậy tuy có hơi kì cục nhưng cũng chẳng ảnh hưởng đến ai.
Nhận xét của Smartjob:
Brandon là một chàng trai thực sự giỏi dù có hơi “dị”. Từ khi học đại học cho đến khi thi tuyển và làm việc tại Google anh hoàn toàn biết cách “quản trị cuộc đời” mình. Việc vay tiền, đi học, học thật giỏi, thi vào Google tất cả đã nằm trong tính toán của anh. Với cuộc sống đắt đỏ tại San Francisco, với khoản nợ khá lớn như thế, tiết kiệm là cách tốt nhất giúp anh rút ngắn thời gian để thực hiện những ước mơ của mình. Các bạn có thể cười chê chàng trai này, cho rằng anh ấy mặt dày, hà tiện,… nhưng theo Smartjob, Brandon là người đáng để học hỏi. Ngay cả Google cũng tôn trọng cách sống của anh thì chúng ta chẳng có lý do gì để cười nhạo một người như thế. Mỗi ước mơ khi đã thành hiện thực đều có những sự đánh đổi và Brandon đã lựa chọn cách đánh đổi như thế.
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
Trong bài viết trước, mình đã giới thiệu với các bạn về Liquibase, một thư viện giúp chúng ta hiện thực việc database migration cho một ứng dụng Java bất kỳ. Trong bài viết này, mình sẽ hướng dẫn các bạn hiện thực database migration sử dụng Liquibase với Spring MVC các bạn nhé!
Mặc định, Liquibase đã hỗ trợ việc integrate với Spring framework. Bằng cách khai báo bean của class SpringLiquibase trong Spring container, khi chạy ứng dụng có sử dụng Spring framwork lên, Liquibase sẽ tự động thực hiện việc database migration cho chúng ta các bạn nhé!
Các bạn có thể cấu hình bean của class SpringLiquibase trong Spring container sử dụng XML configuration (trong tập tin /src/main/webapp/WEB-INF/spring/root-context.xml) như sau:
Từ value của changeLog trong cấu hình của SpringLiquibase ở trên, các bạn có thể hiểu là mình cần tạo tập tin changelog của Liquibase db-changelog.xml trong thư mục src/main/resources.
Mình sẽ định nghĩa nội dung tập tin changelog để thêm bảng clazz như sau: