Cain (8-8) gave up a single and issued a walk in the first inning, worked his way out of that jam, and then gave up just one hit over the next six frames.
Brian Wilson struck out one during a perfect ninth to pick up his 27th save.
San Francisco won for the 12th time in 15 games, but the Giants have only a 12-21 record against division opponents.
The loss went to Rodrigo Lopez (5-9), who surrendered all three runs on five hits in seven innings. Arizona was coming off a three-game sweep of the New York Mets, following a four-game losing streak.
Millions of Getty images now available for free via embed tool
Aaron Carter is still in love with Hilary Duff