I might very well be wrong, but I think only the database approach will guarantee that the invariant is held in a concurrency scenario. Given a scenario where User A and User B both add a product with the same name "PRODUCT" at the same time, then the invariant check in the other approaches might get bypassed, because the duplicate check query for User A's request is run before User B's request has reached the database, and vice versa (there are no products with name "PRODUCT" in the database when the duplicate check queries run).
I might very well be wrong, but I think only the database approach will guarantee that the invariant is held in a concurrency scenario. Given a scenario where User A and User B both add a product with the same name "PRODUCT" at the same time, then the invariant check in the other approaches might get bypassed, because the duplicate check query for User A's request is run before User B's request has reached the database, and vice versa (there are no products with name "PRODUCT" in the database when the duplicate check queries run).